At work, we've been experimenting with Moose because in our enormous and complex codebase we think we can probably benefit a lot from the extra rigour that it brings.
Before I continue, let me note that ours is a typical mod_perl enterprise setup with 6gig of memory per machine, so any memory consumed before the Apache fork is essentially free.
So none of the issues people (including me) have with startup code and memory consumption apply in this case, and I won't be addressing performance issues in this post.
The consensus of the half-dozen people at work is how Moose tries to look like a declarative extension, but doesn't actually act like it.
The following is what Moose seems to have been aiming for.
package Foo;
use Moose;
extends 'Bar';
with 'Role1';
with 'Role2';
has this => (
is => 'ro',
isa => 'Str',
);
has that => (
is => 'ro',
isa => 'Str',
);
1;
Unfortunately, this is what we've had to do instead.
package Foo;
use Moose;
BEGIN { # When we use Catalyst
extends 'Bar';
}
has this => (
is => 'ro',
isa => 'Str',
);
has that => (
is => 'ro',
isa => 'Str',
);
with 'Role1';
with 'Role2';
no Moose;
__PACKAGE->meta->make_immutable;
This "real Moose" code totally spoils the dream of what we felt like we were going to get when we started to play with it.
Most of our current options for fixing this amount to either.
a) Add this extra dependency that will unscrew one of the other of the problems (namespace::autoclean)
b) Use this SECOND heavy sugar layer on top of the FIRST sugar layer, on top of Class::MOP.
Is fixing the syntax or writing light weight sugar really so hard?
As a kind of protest, I tried it for myself and managed to create MooseX::Atom.
This still has some flaws, but the current equivalent of the above would just be this.
package Foo;
use MooseX::Atom [
extends => 'Bar',
with => 'Role1',
with => 'Role2',
has => [
this => (
is => 'ro',
isa => 'Str',
),
],
has => [
that => (
is => 'ro',
isa => 'Str',
),
]
];
1;
You can do the same thing for roles with MooseX::Role::Atom.
Now clearly, this might have some issues. It's the work of an hour and not a whole lot of thought.
But it's still light and clean, with all the class spec in one place up the top where people are used to seeing the declarative stuff in Perl modules.
Perhaps something like this might be a little better...
package Foo;
use MooseX::Hash {
extends => 'Bar',
with => [ 'Role1', 'Role2' ],
default => { is => 'ro' },
has => {
this => { isa => 'Str' },
that => { isa => 'Str' },
},
);
1;
Moose Notes (Score:2)
I wasn't aware that the extends needs to be in a BEGIN block unless you really do have code which needs to happen at BEGIN time. Am I wrong? (Could be).
Also, with accepts a list and using multiple with statements should be avoided unless you have a very, very good reason to do so. A single with statement is like this:
Using it like that gets you the method conflict resolution. Using separate with statements is not only overly verbose, but it composes each role in separate
Re: (Score:2)
Eek, roles behaviour differently depending on whether you do them separately or together?
Yet another reason to be annoyed :(
Re: (Score:2)
Eek, Perl's built-ins behave differently depending on whether or not you call them in scalar context? Yet another reason to be annoyed! ;)
Seriously, it will take you about 3 seconds to get used to this behaviour of the with function and it's behaviour you want.
If you must write an alternative to the Moose API, I strongly recommend that you use Moose for a few months to get really comfortable with it and make sure you understand the design implications. Moose is great and well worth the learning curve.
Re: (Score:2)
It's not a case of getting used to it.
It's that it's a gotcha that the API should never have allowed.
And our team HAS been using Moose for several months. MooseX::Atom is the summary of all the bits of it we don't like.
Re: (Score:1)
Actually there are very good reasons why there are two ways to compose roles.
The ideal way to compose roles is to do it all at once like Ovid showed. This takes better advantage of conflict checking because it first creates a composite role of all the roles passed to
withand then applies that role to the class.The second way is to have multiple
wRe: (Score:2)
No, this does NOT count as "heavy".
Re: (Score:1)
You're not wrong. Extending the parent class needs to happen at compile time in Catalyst because as dagolden mentions elsewhere Catalyst choose once upon a time to support ... hence the BEGIN.
sub foo : Path(/) { }syntax. The nature of that syntax and how Perl goes about making it "work" requires your class hierarchy to be resolved at compile timeAbout the compile time thing (Score:1)
I wonder if the newly released Devel::BeginLift is a way to solve the problem.
I like your MooseX::Atom, but I rather see the problems you mentioned solved without having to rewrite all of my Moose code.
life is short
Re: (Score:2)
Me too
MooseX::Declare (Score:1)
Re: (Score:2)
That would fall into the category of
"b) Use this SECOND heavy sugar layer on top of the FIRST sugar layer, on top of Class::MOP."
Re: (Score:2)
To further clarify based entirely on memory load overhead (using only require, so without any work done in import methods).
semantics vs syntax (Score:1)
First, as a minor nit, you can declare multiple attributes at once, which is a little less repetitive.
As for "with", do you understand in what cases you need to put "with" after you declare your attributes? Even if you do, does everyone on your team (now and in the future)?
The thing I've come to appreciate about Moose is that it expects you to know what you're doing. E.g. you compose roles after attributes when roles require those attri
Re: (Score:2)
The idea of encapsulation is the very reason that we have most APIs.
The idea that you don't NEED to know how something is implemented is always going to be better, as long as that lack of knowledge does not compromise the integrity of the systems or lead to perverted incentives.
What good is a car that requires an intimate understanding of the underlying engine and brakes and gearbox. It might be handy if you are an enthusiast of a racing car driver or pushing the limits in some other dimension.
What good is
Re: (Score:1)
It sounds like you're talking about encapsulation from the standpoint of users and APIs, which is different. Or, rather, it's the same but applied to Moose itself rather than what someone is designing using Moose.
Unlike users, the designers of a system must understand the dynamics of the design. That can't be safely abstracted away. Role composition is a design activity that involves satisfying certain constraints and avoiding certain conflicts in order to get the desired behaviors.
My point was that the
Re: (Score:1)
The composition order of roles is not an implementation detail of Moose; it’s a design concern of the creator of the class into which the roles get composed. To ask for it to be abstracted away is like calling maths is badly designed because
3 + 5 * 2yields different results based on the order in which you evaluate the terms.I regularly take advantage of the runtime has(). (Score:1)
Theoretically, Moose could use Hooks::EndOfScope like namespace::clean, but having a dividing line in the middle of a module can be useful.
If someone would get his act together, we'd have a non-dev release of Perl::Critic::Moose out that can check a lot of this stuff for you, e.g. that you don't use with() multiple times.