I guess that you’ve heard about p5-mop by now.
If not, in a nutshell, p5-mop is an attempt to implement a subset of Moose into the core of Perl. Moose provides a Meta Object Protocol (MOP) to Perl. So does p5-mop, however p5-mop is implemented in a way that it can be properly included in the Perl core.
Keep in mind that p5-mop goal is to implement a subset of Moose, and. As Stevan Little says:
We are not putting “Moose into the core” because Moose is too opinionated, instead we want to put a minimal and less opinionated MOP in the core that is capable of hosting something like Moose
As far as I understood, after a first attempt that failed, Stevan Little restarted the p5-mop implementation: the so-called p5-mop-redux github project, using Devel::Declare, ( then Parse::Keyword ), so that he can experiment and release often, while keeping the implementation core-friendly. Once he’s happy with the features and all, he’ll make sure it finds its way to the core. A small team (Stevan Little, Jesse Luehrs, and other contributors) is actively developping p5-mop, and Stevan is regularly blogging about it.
If you want more details about the failing first attempt, there is a bunch of backlog and mailing lists archive to read. However, here is how Stevan would summarize it:
We started the first prototype, not remembering the old adage of “write the first one to throw away” and I got sentimentally attached to my choice of design approach. This new approach (p5-mop-redux) was purposfully built with a firm commitment to keeping it as simple as possible, therefore making it simpler to hack on. Also, instead of making the MOP I always wanted, I approached as building the mop people actually needed (one that worked well with existing perl classes, etc)
Few months ago, when p5-mop-redux was announced, I tried to give it a go. And you should too ! Because it’s easy.
It’s important to have at least a vague idea of where p5-mop stands at, because this project is shaping a big part of Perl’s future. IMHO, there will be a before and an after having a MOP in core. And it is being designed and tested right now. So as Perl users, it’s our chance to have a look at it, test it, and give our feedback.
Do we like the syntax ? Is it powerful enough? What did we prefer more/less in Moose ? etc. In few months, things will be decided and it’ll only be a matter of time and implementation details. Now is the most exciting time to participate in the project. You don’t need to hack on it, just try it out, and provide feedback.
p5-mop is very easy to install:
curl -L http://cpanmin.us | perl - App::cpanminus
cpanm --dev twigils
p5-mop-redux
project. Otherwise you can get a zip here.cpanm .
from within the p5-mop-redux directory.Here is the classical point example from the p5-mop test suite
This examples shows how straightforward it is to declare a class and a subclass. The syntax is very friendly and similar to what you may find in other languages.
class
declares a class, with proper scoping. method
is used to define
methods, so no sub
there. The distinction is important, because in methods,
additional variables will be automatically available:
$self
will be available directly, no need to shift @_
.$self->accessors
.Functions defined with the regular sub
keyword won’t have all these features,
and that’s for good: it makes the difference between function and method
more explicit.
has
declares an attribute. Attribute names are twigils. Borrowed from Perl6,
and implemented by Florian Ragwitz in its
twigils project on github, twigils are
useful to differenciate standard variables from attributes variables:
As you can see, it’s important to be able to differenciate stuff
(the
variable) and stuff
(the attribute).
The added benefit of attributes variables is that one doesn’t need to contantly
use $self
. A good proportion of the code in a class is about attributes.
Being able to use them directly is great.
Other notes worth mentiong:
BUILD
method, as with Moose.extend
-ing it.SUPER
,
but $self->next::method
.Foo
declared in the package Bar
will be defined as Bar::Foo
.When declaring an attribute name, you can add is
, which is followed by a list of
traits:
ro
/ rw
means it’s read-only / read-writelazy
means the attribute constructor we’ll be called only when the
attribute is being usedweak_ref
enables an attribute to be a weak referencewhich is actually
So, there is no default value, only builders. That means that has $!foo = {};
will work as expected ( creating a new hashref each time ).
You can reference the current instance in the attribute builder by using $_
:
There has been some comments about using =
instead of //
or ||
or
default
, but this syntax is used in a lot of other programing language, and
considered somehow the default (ha-ha) syntax. I think it’s worth sticking with
=
for an easier learning curve for newcomers.
UPDATE: Similarly to attributes, classes and methods can have traits. I won’t go in details to keep this post short, but you can make a class abstract, change the default behaviour of all its attributes, make it work better with Moose, etc. Currently there is only one method trait to allow for operator overloading, but additional ones may appear shortly.
When calling a method, the parameters are as usual available in @_
. However
you can also declare these parameters in the method signature:
Using =
you can specify a default value. In the method body, these parameters
will be available directly.
Types are not yet core to the p5-mop, and the team is questioning this idea. The concensus is currently that types should not be part of the mop, to keep it simple and flexible. You ought to be able to choose what type system you want to use. I’m particularly happy about this decision. Perl is so versatile and flexible that it can be used (and bent to be used) in numerous environment and configuration. Sometimes you need robustness and high level powerful features, and it’s great to use a powerful typing system like Moose’s one. Sometimes (most of the time? ) Type::Tiny (before that I used Params::Validate) is good enough and gives you faster processing. Sometimes you don’t want any type checking.
Because the attribute builder is already implemented using =
, what about
clearer and predicate?
That was pretty easy, right? Predicates and clearers have been introduced in
Moose because writing them ourselves would require to access the underlying
HashRef behind an instance (e.g. sub predicate { exists $self->{$attr_name}}
)
and that’s very bad. To work around that, Moose has to generate that kind of
code and provide a way to enable it or not. Hence the predicate
and clearer
options. So you see that they exists mostly because of the implementation.
In p5-mop, thanks to the twigils, there is no issue in writing predicates and cleare ourselves.
But I hear you say “Wait, these are no clearer nor predicate ! They are not testing the existence of the attributes, but their define-ness!” You’re right, but read on!
In Moose there is a difference between an attribute being unset, and an attribute being undef. In p5-mop, there is no such distinction. Technically, it would be very difficult to implemente that distinction, because an attribute variable is declared even if the attribute has not been set yet.
In Moose, because objects are stored in blessed hashes, an attribute can either be:
That’s probably too many cases… Getting rid of one of them looks sane to me.
After all, we got this “not set” state only because objects are stored in HashRef, so it looks like it’s an implementation detail that made its way into becoming a concept on its own, which is rarely a good thing.
Plus, in standard Perl programming, if an optional argument is not passed to a function, it’s not “non-existent”, it’s undef:
So it makes sense to have a similar behavior in p5-mop - that is, an attribute that is not set is undef.
Roles definition syntax is quite similar to defining a class.
They are consumed right in the class declaration line:
Going meta is not difficult either but I won’t describe it here, as I just want to showcase default OO programming syntax. On that note, it looks like Stevan will make classes immutable by default, unless specified. I think that this is a good idea (how many time have you written make_immutable ?).
Method modifiers are not yet implemented, but they won’t be difficult to
implement. Actually, here is an example of how to implement method modifiers
using p5-mop very own meta. It implements around
:
It is supposed to be used like this:
I would like to see method modifiers in p5-mop. As per Stevan Little and Jesse
Luehrs, it may be that these won’t be part of the mop, but in a plugin or
extension. I’m not to sure about that, for me method modifier is really linked
to OO programmning. I prefer using around
than fiddling with
$self->next::method
or ${^NEXT}
.
Here are some syntax proposals I’ve gathered on IRC and blog comments regarding what could be method modifiers in p5-mop:
around foo { }
method foo is around { ... }
method foo is modifier(around) { ... }
These special variables are pointing to the current instance (useful when
you’re not in a method - otherwise $self
is available), and the next method
in the calling chain. It’s OK to have such variables, but their horrible name
makes it difficult to remember and use.
Can’t we have yet an other type of twigils for these variables ? so that we can
write $^NEXT
and $^SELF
.
Just an idea, but maybe we could have $!public_attribute
and
$.private_attribute
. Or is it the other way around ?
is
? we already have has
!This one thing is bothering me a lot: why do we have to use the word is
when
declaring an attribute? The attribute declaration starts with has
. So with
is
, that makes it two verbs for one line of code. For me it’s too much.
in Moo* modules, the is
was just one property. We had default
, lazy
,
etc. Now, is
is just a seperator between the name and the ‘traits’. In my
opinion, it’s redundant.
Also, among the new keywords added by p5-mop, we have only nouns (class
,
role
, method
). Only one verb, has
.
The counter argument on this is that this syntax is inspired by Perl6:
So, “blame Larry” ? :)
p5-mop doesn’t use @ISA for inheritance, so use base 'Exporter'
won’t work.
You have to do use Exporter 'import'
. That is somewhat disturbing because
most Perl developers (I think) implement functions and variables exporting by inheriting from
Exporter (that’s also what the documentation of Exporter recommends).
You could argue that one should code clean classes (that don’t export anything, and clean modules (that export stuff but don’t do OO). Mixing OO in a class with methods and exportable subs looks a bit un-orthodox. But that’s what we do all day long and it is almost part of the Perl culture now. Think about all the modules that provides 2 APIs, a functional one and an OO one. All in the same namespace. So, somehow, being able to easily export subs is needed.
However, as per Jesse Luehrs and Stevan Little, they don’t think a MOP
implementation should be in charge of implementing an Exporter module, and I
can totally agree with this. So it looks like the solution will be a method
trait, like exportable
:
But that is not yet implemented.
p5-mop is not using the standard scheme where an object is simply a blessed
structure (usually a HashRef
). Instead, it’s using InsideOut objects, where
all you get as an object is some kind of identification number (usually a
simple reference), which is used internally to retrieve the object properties,
only accessible from within the class.
This way of doing may seem odd at first: if I recall correctly, there a time
where InsideOut objects were trendy, especially using Class::Std
. But that
didn’t last long, when Moose and its follow ups came back to using regular
blessed structured objects.
The important thing to keep in mind is that it doesn’t matter too much. Using inside out objects is not a big deal because p5-mop provides so much power to interact and introspect with the OO concepts that it’s not a problem at all that the attributes are not in a blessed HashRef.
However, a lot of third-party modules assume that your objects are blessed HashRef. So when switching to p5-mop, a whole little ecosystem will need to be rewritten.
UPDATE: ilmari pointed out in the comments that there is a class trait
called repr
that makes it possible to change the way an instance
is implemented. You can specify if an object should be a reference on a scalar,
array, hash, glob, or even a reference on a provided CodeRef. This makes p5-mop
objects much more compatible with the OO ecosystem.
Now, it’s your turn to try it out, make up your mind, try to port an module or write on from scratch using p5-mop, and give your feedback. To do that, go to the IRC channel #p5-mop on the irc.perl.org server, say hi, and explain what you tried, what went well and what didn’t, and how you feel about the syntax and concepts.
Also, spread the word by writing about your experience with p5-mop, for instance on blogs.perl.org.
Lastly, don’t hesitate to participate in the comments below :) Especially if you don’t agree with my remarks above.
This article has been written by Damien Krotkine, but these people helped proof-reading it: