Flutterby™! : Moose misconceptions

Next unread comment / Catchup all unread comments User Account Info | Logout | XML/Pilot/etc versions | Long version (with comments) | Weblog archives | Site Map | | Browse Topics

Moose misconceptions

2012-12-18 21:34:39.699269+00 by Dan Lyke 4 comments

Over on a Twitter conversation about the awesomeness that is XML::Rabbit, John asked me what I didn't like about Moose. And to tell you the truth, I'm not totally sure. My observations so far:

  • I believe, though I could be mistaken, that $row=MooseObj->new(%{$sth->fetchrow_hashref}) is substantially slower than $row=$sth->fetchrow_hashref.
  • I also have the impression that I've been bitten by versionitis and features in older vs new Moose, such that I'm reluctant to attempt it in places where I may need to deploy on older machines.
  • I know that I've gotten confused and wrapped in knots while attempting to set up additional typing stuff, specifically for trying to do automated wrapping and unwrapping of geographic types for storage in SQL.

I know most of these are misconceptions and issues in my understanding of the system, my hope is that by putting them here I can get a little better conversation than 140 chars at a time allows.

[ related topics: Web development John S Jacobs-Anderson Content Management Invention and Design Databases ]

comments in descending chronological order (reverse):

#Comment Re: made: 2012-12-21 00:25:37.215749+00 by: Dan Lyke

I guess it's more that the objects contain the data and the operations on them, but are not the data. It's a matter of de-coupling the objects. Making a sharper distinction between the Model and the Controller. Let the model be hashes loaded from the database and manipulated by SQL, and don't assume that there'll be some sort of controller object instantiated per model object.

So, yeah, something like "here's a list of criteria, for each of the potential classes of data pass those criteria to that controller". Let that level know about SQL and column names and all of that stuff, don't try to find commonality between those different types unless they really have similar representations, let the controller deal with it.

Example: Rather than having an iterable list of all objects, all of which have an ISO8601 date, have a set of controllers which know about their objects and will take the input date and convert it to a Unix timestamp for comparison to a file stat() return, and another that generates the SQL query, and so forth. Which makes sense, let the damned query engines do what they do best.

#Comment Re: made: 2012-12-21 00:01:24.228314+00 by: John Anderson

In reverse order this time:

* Right, that approach ends up being easier to maintain and to understand what's going on. Type coercions are whizzy and cool looking but practical issues abound.

* So, sounds like the second issue is more about $YOUR_WORK's technical debt as far as deployment. A reason not to use Moose there, but not a reason not to use Moose.

* I'm not quite sure about what you mean about "making my objects operations" -- or, more precisely, I don't see what that would get you over straight-up procedural classes that operated on your data. I mean, if an object is code + data, and your object represents an operation that's getting passed it's input, what data are you providing when you instantiate an instance of your object? I guess you could effectively give it a config and then use that to effectively curry a bunch of methods in the object -- is that the sort of thing you're thinking of?

#Comment Re: made: 2012-12-20 17:31:52.870807+00 by: Dan Lyke

For the first, I think that perhaps one of the ways I need to rework my OO design thinking is moving from the C to C++ progression of "your data structures are now objects" to a place in which the operations are objects, and the data and data structures remain in the fairly primitive systems.

In particular, my Wiki system that runs Flutterby.net has a bunch of objects which are backed up by and cached in an SQL database, which means I do a lot of my first example there. I attach more code to those objects, but really I should be making my objects operations which take those database records in place, and get out of the C++ mindset of trying to map them one-to-one onto objects.

On the second: Work is getting better about deploying everything into VMs as an alternative to keeping ancient systems running, but there's lots of machines with pre 5.10 (which I know because of funkiness with the // operator), and there are huge brittle systems into which I'm putting new features and I'm never sure which code is going to go where or what library set I can let things run on.

On the last: Yeah, right now I've got a "turn into SQL" and "marshall from SQL" that nows about the various types and unpacks things like PostGIS datastructures into arrayrefs of arrayrefs.

#Comment Re: made: 2012-12-20 03:35:44.525909+00 by: John Anderson [edit history]

Okay, so, I guess I should have some disclaimers first. I don't use Moose in everything I write -- but I do tend to reach for it as soon as things get the slightest bit OO. It's just easier. Also, my boss at $CURRENT_GIG is the guy who invented it, and one of the guys I (ostensibly) manage is the current maintainer. My biases, I hope, are clear.

In your first point, you have to understand it's not an apples-to-apples comparison. In the first version, you're instantiating an object. You may be doing substantial input validation, making sure that certain attributes are provided, and who knows what else. In the second example, you're ... making a hashref. That's it. No attribute accessors, no real guarantee about what's there, etc. Now, depending on what you're going to do with $row, either of those could be a valid thing to do. ENEEDMORECONTEXT.

That said, a more fair comparison would be my $row = MooseObj->new( $sth- >fetchrow_hashref ) versus my $row = NonMooseObj->new( $sth->fetchrow_hashref ) -- and then you can start to think about all the stuff you'd need to do in NonMooseObj.pm to get the stuff you get (batteries included!) with Moose. You may not use all of the features all the time, but the parts you almost certainly will use constantly (attribute accessors/mutators, requiring attributes, maybe some lazy generation of derived attributes, etc.) are annoying to have to write yourself every time, and easy to mess up. With Moose, on the other hand, you're using a robust and well tested code base that some of the better minds in Perl have slaved over to make sure it's as fast as it can reasonably be while still doing what it needs to do.

For your second issue: why would you ever use anything other than the most recent stable version? We have local::lib and cpanm -L and dzil now, it makes it much nicer to have an application that contains copies of all its own dependencies, so you can deal with stuff like this in a reasonable way, even if you don't have the operations level support you'd like to have.

As far as elaborate type constraints and coercions, I've found basically the same thing -- but as a result, I mainly avoid those when I can. (And my impression is that's sort of the consensus on them among the Moose-beards.) The only place where I might reach for one is if I'm trying to feed the output of something I don't control into one of my classes, and doing it as a type coercion made more sense than having two distinct attributes -- but I suspect that I'd probably go the two attributes route. Something like:

has external_attr => ( is => 'ro' , isa => 'External::Output' , required => 1 );
has my_attr => ( is => 'ro' , isa => 'HashRef' , lazy => 1 , builder => 'build_my_attr_from_external' );
sub build_my_attr_from_external { 
  my( $self ) = @_;
  # code here to munge $self->external_attr into the form I really want it to be in
  return $munged;

I think we're pretty rapidly approaching the "sling code samples" around point... 8^)