|Perl: the Markov chain saw|
Re^3: Reopening builtin classes, redefining builtin functions?by stvn (Monsignor)
|on Dec 09, 2007 at 22:24 UTC||Need Help??|
Moose is based on Class::MOP which is a Meta Object Protocol written in Pure Perl. It is also a meta-circular MOP, which means that at some point it bootstraps and redefines part of itself using itself. The end result is that Class::MOP::Class is itself an instance of Class::MOP::Class (to be more technically correct, Class::MOP::Class->meta is an instance of Class::MOP::Class because a class name in Perl is not an object, but just a string and so we need to store the metaclass instance somewhere). Again, this is all defined in Pure Perl, so it is 100% accessible to your regular Perl code. Ruby defines Class in object.c (as well as Module, Object and a few other core bits), and the guts of these things are only as accessible as the language wants them to be (which is quite a lot, but not nearly as much as Class::MOP though). This difference is critical to the power of the MOP.
In Ruby, in order to get the metaclass, you have to do the idiomatic class <<self; self; end, and then you can hack on the metaclass. This is tricky and can get really ugly really quickly (DISCLAIMER: I am not an expert Ruby hacker, so there may be some secrets that I am missing out on, but I found little online or in books to contradict this statement). Most of the stuff I have seen which does anything interesting with this, ends up using the *_eval methods found in Module. Now, I have no problem with string-eval-ing code when you are deep in the meta-world, I do it a lot in Moose to optimize accessors and such. However in Moose it is a choice, in Ruby it is not. This means that there is a fundamental limit to how reusable your metaclass trickery can be and more importantly how maintainable it is (string evaled code is really annoying to maintain, I know).
One of the key things you can do in Moose that (AFAIK) you cannot do in Ruby is to extend the meta layer. Ruby gives you access to the metaclass and through modules and mixins you can somewhat alter a classes behavior in a reusable fashion. But thats where it ends, with Moose you can literally extend Moose with Moose. Here is a classic metaclass example of a class which counts it's instances.
Then in your code you can do:
An equivalent Ruby version would require some serious meta-programming to both wrap the new method, as well as create and install a custom metaclass (honestly I have no clue as to how it would be done, and even if it could be done).
(Yes, I know thats a contrived example, but this is a simple example to illustrate my point, there is lots of useful and amazing metaclass hackery going on in the #moose channel on irc.perl.org, stop by if you want to see some).
Now, aside from metaclasses, Moose (though Class::MOP) also provides some sub-protocols. The most powerful one (IMO anyway) is the Attribute protocol, you can find a nice cookbook entry for it here which goes into a lot of detail about how you can easily extend this protocol to get some nice features. You can also use the Instance protocol to customize the instance type which Moose uses, which is by default a HASH ref. The module MooseX::GlobRef::Object provides an excellent example of how to extend this protocol to make your classes use a GLOB ref. Of the cool parts about this is that it works on a per-class basis, and is not a system wide change, so it won't break your other stuff.
Now throw into this Roles, which are a more sane version of mixins, the fact that you can have true Multiple Inheritance if you want it, and most importantly (at least for me) access to the CPAN (yes, I know, rubygems, etc,.. but it is no CPAN).