You do realize that you can write bad code with any module, not just Moose. Just because Moose makes things a little easier and standardizes/formalizes a few concepts does not mean it forces you to then go use those things badly. Seems to me that is the fault of someone not understanding good OO design and/or abusing the features provided by the tool. This really isn't Moose's fault, it is the users.

So Moose doesn't really allow you to talk about object attributes unless you write (or, much more likely, generate) accessor methods.

Actually, Moose advises you that accessors are a good way to encapsulate attribute access, but if you don't want to generate an accessor you don't have too. And if you want to then do direct HASH access internally, you can. Moose does not stop you from doing that, nor does is it impose any penalties and not allow you to take advantage of other things like managed instance construction, etc.

So OO design using Moose will almost always revolve around, primarily focus on what attributes you want your object to have and the accessor methods for them. And that leads to class design that is mostly glorified hashes (or Pascal 'record's, aka. C 'struct's). And I've seen that suck in the long run so many times.

Actually, if you do proper OO design, then no, this is not true. You are blaming the tool for the mess the user made with it.

Implement attributes as constants so you get compile-time catching of typos on their names. Make internal accessing of attributes actually look different and a bit ugly, so you know the difference and you keep internal implementation details internal.

I agree that making everything methods does kind of make everything blend together, and that Perl's highly "virtual" approach to method dispatching makes it very hard to catch typos. However the encapsulation is kind of a good thing.

Delegate rather than inherit when combining classes.

Sure, Moose makes this easy too with the handles attribute option.

If you start slipping back into type-based validation (as you are almost pushed into doing as use more Moose), you will find yourself more and more doing C++/Java-style design.

Again, you are not pushed into using types in Moose, it is an option.

You'll end up with classes as bags of attributes with accessors and then you'll try to put protections around those accessor by adding "types" and adding 'before' and 'after' wrappers. Your logic for a single class will be split apart into tons of tiny pieces where the order and interactions will become almost impossible to see, predict, adjust, and debug.

Again, don't blame the tool for someone being stupid. Nothing about Moose forces you into doing something as idiotic as you describe. Just because a feature exists doesn't mean you have to use it, and most certainly does not mean that you should abuse it.

(Yes, I'm actually working on several simple tools to make good OO design more convenient in Perl 5. For example, delegation of a whole list of methods should be something trivial to write, almost as trivial to write as 'use base "Thingy";' or 'does CoolStuff;'. But even while it isn't trivial in Perl 5, it also isn't particularly hard. Just a bit inconvenient.)

Where is the code? I would love to have a look.

-stvn

Comment on Re^2: How Large Does Your Project Have To Be to Justify Using Moose? (modular)

First, let me stress that I think you should be very proud of Moose. It is a stellar accomplishment. Really.

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.

I'll go to a simple analogy. I was not arguing, "Please don't use Acme brand hammers because I've seen people trying to drive screws in with a hammer and that is a bad idea". I was more arguing "Nails relatively suck compared to screws when it comes to holding two pieces of wood together. Sure, it is much easier up front to pound a nail in with one good whack, but in the long run, you'll be happier with how well the two pieces of wood stay held together if you take the time to twist a good screw all of the way in. And this makes it not the best allocation of resources to set up to use an extremely well-made Acme pneumatic nail driver that can't really be used unless you also pull in a whole trailer full of gas generators and compressors. And I know this because I've seen smart, professional carpenters nail themselves into a corner in ways that were very easy for them because they didn't even have to do any pounding in order to toss another nail into the pieces of wood that they were trying to assemble."

The worst part of that analogy (IMHO) is that if I set up an Acme pneumatic nail driver and use it to make a cheap little birdhouse, then I can give that birdhouse to my grandmother and she can just hang it up in her tree without even needing a K-Mart hammer. But if I build a module using Moose, then anybody who wants to use that module is expected to do the equivalent of setting up their trailer full of generators and compressors and then to wait for at least some of the compressors to come on-line each time they want to use my module.

Nothing about Moose forces you into doing something as idiotic as you describe.

That particular "idiotic" thing I described is pretty close to what was proposed very recently by somebody smart who I respect when they talked about how they would go about doing what we are instead doing much more "by hand" (in part because Perl seems to still lack a simple, small, rechargeable screwdriver and in part because, at a certain scale, a custom tool can fit so much better than an amalgam of general-purpose components, even when those components are quite well-built, and our problem is at that scale).

Are you telling me that Moose documentation discourages the use of pre and post method wrappers and generated accessors with type-based constraints? Since you chose to describe that as "idiotic", surely the documentation discourages it. Most certainly, the documentation can't encourage it!

If I go to pick up Moose (much as the original poster in this thread suggested), the documentation will naturally encourage me to think about design first in terms of the interface I want to provide and will surely give me good advice about keeping attributes hidden behind that interface.

I'm not so foolish as to think that Moose forces bad design. But surely, Moose won't encourage bad design. Surely, it won't just immediately suggest to me that I should start with type-enforced, auto-generated accessors with before/after wrappers and inheritance! Why, that's most of the way to what you called "idiotic" combined with what every wise old hacker I check with knows is just plain a bad idea (inheritance).

So, you can claim that only stupid people would do stupid things with Moose. But my experience is quite at odds with that (though that experience covers more than just Moose; it also covers other tools that make auto generation of accessors easy).

Certainly, exceptionally smart and careful people can make good use of some features of Moose without ever being lured into the problematic but easy routes. But that doesn't make Moose the best nor cheapest way to accomplish those smart things.

Where is the code? I would love to have a look.

It is not like I'm hiding it. I'm working on it. I release parts as I am able to make them release-worthy. I discuss the ideas fairly often and provide example code as well. Some of the current bottleneck is figuring out which directions scale better to more complex designs. Plus the past couple of months have been less productive due to burn-out (which I seem to have finally broken out of just recently). Much of the code will likely hit CPAN as individual modules. I expect most of the modules to be useful in isolation (and to not require each other) even though I plan to use them together. I also expect them to be quite small. Some code experiments will get posted at PerlMonks before CPAN.

But there are also some fundamental "direction" decisions that I have to sort out. And there is also the wordsmithing of prose and examples in hopes of framing some of the overarching principles to help people be more likely to understand what I'm trying to get at.

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.

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:

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, ...

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).

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.

Most of the interface of my ancient module is not centered around the attributes (once the information is passed in via new(), that's pretty much it). The majority of the methods are there messing with the object state, and the remaining soon-to-be-implemented-as-plugins methods are there for different types of output. Is Moose the right tool for this?