Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re^4: How Large Does Your Project Have To Be to Justify Using Moose? (encourage)

by stvn (Monsignor)
on Oct 11, 2011 at 01:31 UTC ( [id://930723]=note: print w/replies, xml ) Need Help??


in reply to Re^3: How Large Does Your Project Have To Be to Justify Using Moose? (encourage)
in thread How Large Does Your Project Have To Be to Justify Using Moose?

I will try to respond more when I am not suffering from a post-PPW head cold, however I will say one thing.

Oops. Wow, the encouragement didn't take any effort at all to find.

The goal of that example is to show as many features in as small a space as possible and to look cool doing it. Think of it more as the 17 LoC elevator pitch to get your Ruby friend to stop calling Perl line noise.

... well I will say two things actually, but I can't be held responsible for any of possible NyQuil induced ranting which may follow.

As for the whole anti-accessor topic, I won't really disagree with you, having to wrap all your attributes with accessors is just ugly. But that said, the combination of hash autovivification and direct hash access is, in my opinion, even uglier. And while Inside-Out object had a lot of promise, they failed to deliver.

Where you look at Moose attributes and see C-struct style OO, I see something much different, which is class managed state. It sometimes seems funny to me that people focus so much on the accessors, which I see as useful, but what I find infinitely more useful is the fact that object construction is done for me, and done correctly. That was my biggest pet-peeve about Perl OO before I wrote Moose.

Now, I know you also are not a big fan of inheritance and think it is grossly over-used and horribly over-taught. I agree with you on this too, my personal rule is that, unless the domain model actually calls for it, meaning this is how it works in real-life, then an inheritance hierarchy should never be more then 1 (max. 2) level deep. (You are welcome to go look to see if I have violated this in any of my CPAN modules, I probably have, but hey, ... he who is without sin cast the first stone).

Now, I'd be more convinced by the standard "don't blame the tool" arguments if I had argued that one shouldn't use Moose because it fails to prevent you from doing stupid things. I'm a Perl programmer; having the freedom to do things previously considered stupid is something I highly value.

... snip rant about some crazy hammer which I TOTALLY am gonna buy tomorrow (and get two for chromatic as well) ...

Anyway, the point is (or at least what the NyQuil is telling my brain is the point) is that Moose is just a tool, an what I failed to convey in my response and I think (based on your response) you failed to infer from my response which failed to be conveyed inside of. Is that Moose is much more multi-faceted then you have either known about, or given it credit for. Allow me to try and illustrate with some code.

package Point; use Moose; has 'x' => (is => 'bare', isa => 'Int'); has 'y' => (is => 'bare', isa => 'Int'); sub move { my ($self, $direction, $distance) = @_; $self->meta->name->new( x => ($direction eq 'x' ? $self->{x} + $distance : $self->{x}) +, y => ($direction eq 'y' ? $self->{y} + $distance : $self->{y}) ); } sub scale { my ($self, $factor) = @_; $self->meta->name->new( x => ($self->{x} * $factor), y => ($self->{y} * $factor) ); }
So first thing to point out is that there are no generated accessors, which does mean we have to resort to plain HASH access, but while this is not considered best practice, it is a choice you have every right to make. Now you alluded to something that sounded like Inside-Out objects in an earlier statement, which unfortunately Moose does not easily support (you can inherit from them using MooseX::InsideOut, but not code with them directly with the technique). Personally I would have generated private accessors here, like so:
has 'x' => (accessor => '_x', isa => 'Int'); has 'y' => (accessor => '_y', isa => 'Int');
but again, this is all your choice, if you so choose.

Next, notice the complete lack of new because (IMO anyway) instance construction is not something the user should ever be burdened with. Not only will Moose DWIM in the face on inheritance (yuk, I know you hate that word), but it will also DWIM for Role composition (and if you hate inheritance you might really like roles). But also in this particular example, it will also check the types of the input. See, these types are not just for accessors, but also for constructors. Which means that Moose has constructed your instance for you, placed all the values in the expected slots, checked those values to be sure they match the expected types, blessed the HASH ref for you and ran all initializer routines for you (in the correct (reverse inheritance) order).

So, next, lets look at our methods, since we don't have accessors, you will see a bunch of direct HASH ref access (a choice is a choice and as long as you make it with knowledge it is ok with me even if I wouldn't do it that way, if you want to marry a fire hydrant, who am I to say no). ... and we added a few other methods here, because I want to show that Moose does not have to be all about the attributes to still provide some benefits.

As you can see in move and scale, we simply create a new instances based on the transformations requested. Of course we could also have manipulated the internal state of the instance, this is again another choice, but IMO when you are hiding implementation this much, then going the "immutable object" route just feels better. Additionally we get a few other benefits from this, ...

  1. We can use Moose to introspect the proper class name. This will not only work when inherited (which __PACKAGE__ won't), but it will also stand up in the face of an overloaded ref (where ref($self) wont, see UNIVERSAL::ref for more info) and it will also work even if you had applied a Moose Role to your instance at runtime (which does some insane stuff in a very sane and controller manner to accomplish that).
  2. Because we are creating a new instance, our constructor parameters are getting type checked again. Which means we can (if we want) skip checking the types of $distance and $factor because if they destroy the Int-ness, things will complain loudly. (this is also where "immutable objects" come in handy because the original instance is untouched).

Whoa man, this is some goooood NyQuil.

So, anyway, my point to you is that Moose can be an extremely effective Sonic Screwdriver if you just stop using it as a hammer long enough to notice.

-stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://930723]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (6)
As of 2024-03-19 07:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found