MooseX::Extreme Needs a New Name

On github, I've released MooseX::Extreme.

It's based on years of experience being the lead designer of the Corinna project and trying to figure out how we can get a version of Moose which is safer and easier to use, including removing a lot of boilerplate. This code:

package My::Class {
    use MooseX::Extreme;

    ... your code here
}

Is sort of the equivalent to:

package My::Class {
    use v5.20.0;
    use Moose;
    use MooseX::StrictConstructor;
    use feature qw(signatures postderef);
    no warnings qw(experimental::signatures experimental::postderef);
    use namespace::autoclean;
    use Carp;
    use mro 'c3';

    ... your code here

    __PACKAGE__->meta->make_immutable;
}

There's not need to end packages with a true value and MooseX::Extreme makes your class immutable for you.

But what's allowed in the constructor? I've regularly face the following problem:

package Some::Class;

use Moose;

has name     => (...);
has uuid     => (...);
has id       => (...);
has backlog  => (...);
has auth     => (...);
has username => (...);
has password => (...);
has cache    => (...);
has this     => (...);
has that     => (...);

Which of those should be passed to the constructor and which should not? Just because you can pass something to the constructor doesn't mean you should. Unfortunately, Moose defaults to "opt-out" rather than "opt-in" for constructor arguments. This makes it really easy to build objects, but means that you can pass things to the constructor and it won't always work the way you want it to.

There's an arcane init_arg => undef pair to pass to each to say "this cannot be set via the constructor," but many developers are either unaware of this is simply forget about it. MooseX::Extreme solves this by separating has into param (allowed in the constructor, but you can also use default or builder) and field, which is forbidden in the constructor. We can rewrite the above as this:

package Some::Class;

use MooseX::Extreme;

param name     => (...);
param backlog  => (...);
param auth     => (...);
param username => (...);
param password => (...);

field cache    => (...);
field this     => (...);
field that     => (...);
field uuid     => (...);
field id       => (...);

(Note: has is still available)

And now you can instantly see what is and is not intended to be allowed in the constructor.

It works with Perl versions from v5.20.0 onwards, has CI setup on github against all major Perl versions it supports, and even has some interesting fixes to make it work in the debugger.

In short, I'm trying to apply a list of safe (and conservative) defaults to Moose--I've deliberately omitted some features that would probably be overkill--and mostly just uses normal, sane modules.

It still needs more tests, so I won't release it right away, but it reduces boilerplate and and applies defaults that are generally popular. So what I should I rename it to before releasing to the CPAN?

4 Comments

MooseX::Std? ๐Ÿ˜›

(My thanks go to the various community personalities involved for making this car crash of an in-joke possible. ๐Ÿ˜›)

MooseX::Modern::v2022 MooseX::Best::Practice::v2022

regarding Modern / Best::Practice part: Intention is to provide some kind of set of best practices.

regarding v2022 part: everything evolves - there may be different set of rules next year or in 10 years, but it should not be forced to every module using it. This kind of modules imho must have constant spec and should be versioned by name allowing partial adoption in project to mitigate enforced total costs.

Hi Ovid,

I would suggest MouseX::PreCorinna -- this makes it explicit that it loads a bundle of modules that follow more or less the same design choices as Corinna.

Regarding the 'param' and 'field' extensions : yes it's desirable to remove boilerplate, but in my experience I found out that I do not always have the same boilerplate in every class, because attribute definitions have several dimensions. Passed or not to the constructor is one, but there are also others, like lazy/non-lazy, having accessors or not, etc.

So what works best for me is to declare at the beginning of each class the minimal syntactic sugar that is useful for that class. Yes there is some repetition between classes, but readers can immediately see what kind of attributes are there, without looking at another source. Some examples of such ad_hoc extensions :

sub has_lazy  ($@) {my $attr = shift; has($attr => @_, lazy => 1, builder => "_$attr")}
sub has_inner ($@) {my $attr = shift; has_lazy($attr => @_, init_arg => undef)}
sub has_slot  ($@) {my $attr = shift; has($attr => @_, is => 'bare', init_arg => undef)}

Leave a comment

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/