Saturday, June 13, 2009

Why make_immutable is recommended for Moose classes

Someone on perlmonks asked
Can you point me to a good explanation of why make_immutable is recommended?
And I realized in the documentation we really only say (in Moose::Manual::BestPractices)
making classes immutable speeds up a lot of things, most notably object construction.
So instead of burying my explanation deep inside Perlmonks I thought I would explain it here (and add to my Iron Man creds).

So, Moose metaclasses are built specifically so that they can be altered at any time from anywhere and still remain a valid and correct class. This is why there is no __PACKAGE__->finalize_class or similar type of method call required at the end of your Moose class definition. But doing things this way does come at a price in that some of the meta-level calls can be very expensive.

For instance, if you wanted to know all the attributes supported by a class, you would need to collect all the local attributes, then visit each superclass (recursively) and collect all those attributes while being sure to skip all overridden attributes. This can get quite expensive and since we allow for you to, at any time, alter the inheritance structure or add/delete attributes via the MOP, this means we can not cache the results of that query (well we could cache it, but then we would have to have all sorts of extra code to check the cache and invalidate it, etc. etc.).

So what you are doing when you make a Moose class immutable, is actually saying "it is okay to cache things, I am not going to mess with the metaclass". At that point Moose takes the opportunity to memoize many of the MOP calls and install methods that throw exceptions when you try and alter the metaclass, effectively making the class read-only. However, this really only helps speed up calls to ->meta methods, so we also then take it one step further.

The example I gave above, of checking all attributes in a class, may seem kind of esoteric and not something one usually needs to care about, but this is exactly what Moose needs to do every time it creates an instance of an object. It needs to do this in order to properly initialize all the slots in an instance, fire any triggers, check any type constraints, perform any type coercions and call all BUILD methods in the inheritance graph in the correct order. By memoizing the computed list of all inherited attributes we are actually saving quite a lot of computation, but honestly that is not enough. So we actually take the opportunity to inline and compile our own optimized constructor method that does the exact same thing, but in much less time. The result is that object construction is significantly faster during the runtime of the program (which is when it really counts) and we instead take the compile-time hit of the code construction and evaluation. And since we are in there already we also inline a DESTROY method which correctly calls all the DEMOLISH methods in the correct order (Moose already, by default, will inline your attribute accessors, but if it didn't then it would do that as well).

So the short answer is that making your class immutable is good because it memoizes several metaclass methods and installs an optimized constructor and destructor for your class and therefore helps reduce a fair amount of the cost (during runtime) of all the abstraction that the MOP provides.

Monday, June 8, 2009

YAPC::NA Moose Hackathon

We are planning a Moose hackathon after YAPC::NA this year. It should be a nice compliment to Dave Rolsky's Moose course as well as the several Moose related talks on the schedule, so if you can stay an extra day or two in Pittsburgh come and hang out and talk/hack some Moose.

Saturday, June 6, 2009

Moose Bus Factor

One of the most common problems in many software projects is the Bus Factor. This can be especially true of Open Source projects even if they have a lot of contributors, because it is not often that a project will get another developer who is as steeped in the code as the original author.

I am happy to say that the Bus Factor for Moose is now a solid 2 and able to expand easily up to 5 and Class::MOP is the same if not better. This is not to count out the other 50+ contributors at all. Several of them have been steadily climbing in these lists over the past few months.

Here is the output of git-blame for Moose.

Total lines: 45996
       18954  41.21%  Stevan Little
       15270  33.20%  Dave Rolsky
        3556   7.73%  Yuval Kogman
        2414   5.25%  Shawn M Moore
        1038   2.26%  Chris Prather
         414   0.90%  John Napiorkowski
         387   0.84%  Tomas Doran
         333   0.72%  Guillermo Roditi
         298   0.65%  Jesse Luehrs
         274   0.60%  Todd Hepler
         259   0.56%  Lars Dieckow
         259   0.56%  Hans Dieter Pearcey
         195   0.42%  Anders Nor Berle
         187   0.41%  Matt S Trout
         186   0.40%  Nathan Gray
         186   0.40%  Aankhen
         182   0.40%  Jonathan Rockway
         177   0.38%  Jesse Vincent
         172   0.37%  Aran Clary Deltac
         150   0.33%  *initial checkin
         110   0.24%  John Goulah
         103   0.22%  Tomas Doran (t0m)
          89   0.19%  Dann
          88   0.19%  Marcel Grunauer
          88   0.19%  Ricardo SIGNES
          73   0.16%  Jess Robinson
          63   0.14%  Evan Carroll
          56   0.12%  Justin DeVuyst
          55   0.12%  Ash Berlin
          50   0.11%  Daisuke Maki (lestrrat)
          46   0.10%  Wallace Reis
          41   0.09%  Florian Ragwitz
          40   0.09%  Tokuhiro Matsuno
          33   0.07%  Adam J. Foxson
          32   0.07%  Marc Mims
          23   0.05%  Robert 'phaylon' Sedlacek
          18   0.04%  Cory G Watson
          18   0.04%  Scott McWhirter
          15   0.03%  Paul Jamieson Fenwick
          13   0.03%  michaelr
          11   0.02%  Tomas Doran (t0m
           7   0.02%  Adam Kennedy
           5   0.01%  Robert Boone
           5   0.01%  Nelo Onyiah
           4   0.01%  t0m
           4   0.01%  Mike Whitaker
           2   0.00%  Piotr Roszatycki
           2   0.00%  Eric Wilhelm
           2   0.00%  Paul Driver
           2   0.00%  Christian Hansen
           2   0.00%  hakim
           1   0.00%  Marcus Ramberg
           1   0.00%  John Douglas Porter
           1   0.00%  Jay Hannah
           1   0.00%  Shlomi Fish
           1   0.00%  Cory Watson

And here are the same for Class::MOP.

Total lines: 26107
       11109  42.55%  Stevan Little
        5107  19.56%  Dave Rolsky
        4525  17.33%  Chris Prather
        1919   7.35%  Yuval Kogman
        1606   6.15%  Florian Ragwitz
         702   2.69%  Guillermo Roditi
         647   2.48%  Shawn M Moore
          98   0.38%  nperez
          89   0.34%  Matt S Trout
          70   0.27%  Ricardo SIGNES
          50   0.19%  *initial checkin
          42   0.16%  Tomas Doran
          36   0.14%  Todd Hepler
          29   0.11%  Hans Dieter Pearcey
          26   0.10%  Jesse Luehrs
          24   0.09%  Marc Mims
          11   0.04%  Brandon L Black
           5   0.02%  Robert Boone
           4   0.02%  Scott McWhirter
           3   0.01%  Jonathan Rockway
           2   0.01%  Flavio Poletti
           2   0.01%  Shlomi Fish
           1   0.00%  Rob Kinyon

I am quite happy with this, as it now means that I can cross the street without fear.


Wednesday, June 3, 2009

Moose and DWIMery

So Ovid recently discovered that Moose does not create any accessors by default. Which was surprising to him and truthfully has surprised many people over the years. We on #moose have discussed this many times and the general consensus has always been to leave it as it is. I explain in the comments to Ovid's post why this is so, but I figured that for my inaugural blog post I should expand on this topic.

DWIMery and the Slippery Slope

DWIMery ("Do What I Mean"-ery) can be a very valuable thing when designing APIs but it does come at a cost. The more specific your API, the easier it is to DWIM since the option set is likely pretty small and defaults are usually obvious. But the more general your API, the harder it is to strike a balance. The problem gets even more so when you are designing something like an object system or a language. A system like Moose needs to not only support doing what I mean, but also doing what everyone else means as well. 

Opinionated Software vs. TimToady

Recently there has been a trend towards more "opinionated" software (Ruby On Rails) and even "opinionated" languages (Python).  The popularity of both these pieces of software shows that many people like this trend. However, Moose is Perl, and in Perl we subscribe to TIMTOWDI (There Is More Than One Way To Do It). On some level, you could say that opinionated software is actually the antithesis of TIMTOWTDI.

Now this is not to say that Moose is not opinionated or is somehow the pinnacle of  TIMTOWTDI. In fact Moose is actually pretty opinionated and I strongly believe that too much TIMTOWTDI is one of the reasons that Perl has the negative reputation it has for maintainability and code clarity. But what Moose does differently is to be humble about its opinions and make it easy (for some value of "easy") to override those opinions and inject your own.  

Chris Prather actually suggested just such a solution in one of his comments to Ovid's post. The syntax looks something like this:

package Foo;
use Moose -traits => ['ReadOnly'];

has 'bar';
has 'baz';

This could be accomplished by making a "trait" (the Moose term for a role that is applied to a meta-level object) which would affect the metaclass such that any time an attribute was created it would force a default read-only accessor to be created. While this sounds complicated it would actually be fairly simple, the trickiest part being dealing with merging your default read-only-ness with any user specified options.

Why Moose doesn't create accessors by default

Moose has always aimed to be as Perl-ish as possible, which means trying to embody the spirit of TIMTOWTDI. As I mentioned in one of my responses to Ovids post, the choice of which type of accessors Moose should create is not so simple. My personal inclination is towards generating simple read-only accessors, others might expect read/write accessors to be the default (which is what other common Perl OO modules like Class::Accessor provide). But this ignores the suggestions that Damian made in Perl Best Practices or the people who like semi-affordance accessors (->foo for reading and ->set_foo for writing) or the people who prefer public readers/private writers. The list can go on and on, and each and every one of these is an equally valid choice. 

In my mind the only solution when faced with all these differing and equally valid viewpoints is to actually favor none of them, but allow all of them. And of course, this is exactly what Moose does. I believe that this is most in keeping with the spirit of TIMTOWTDI and therefore the most Perl-ish.