Telephone +44(0)1524 64544
Email: info@shadowcat.co.uk

Half A Moose

OMGMooseIsTehSl0wz0rr!!!!11ONE

Thu Jan 28 19:45:00 2010

Digg! submit to reddit Delicious RSS Feed Readers

Once again, cometh the "oh noez Moose is slow" silliness, this time courtesy of a post by Ovid proposing we need a role system without the rest of Moose, so I thought for this week's Iron Man post I'd break down and explain how he's CONSIDERIN IT COMPLETELY WRONG.

We already have a role system that can be used without the rest of Moose. It's called Moose.

There is absolutely no reason you can't do:

  package Foo::Manual::Bar;

  use Moose::Role;

  sub bar {
    my $self = shift;
    return $self->{bar} unless @_;
    $self->{bar} = $_[0];
  }

  package Foo::Manual;

  use Moose;
  extends 'UNIVERSAL'; # get rid of Moose::Object

  with 'Foo::Manual::Bar';

  sub new { bless {} => shift }

and have it work Just Fine.

So now you've just used a role, but all your code is manual. In fact, we can do better than this, since Moose is ultimately controllable -

  package Foo::Manual;

  use Moose;

  with 'Foo::Manual::Bar';

  sub new { bless {} => shift }

  sub DESTROY {} # override Moose::Object::DESTROY

  __PACKAGE__->meta->make_immutable(
    inline_constructor => 0,
    inline_destructor => 0
  );

and now we have access to the ->does method and friends, but are still not using any of the rest of the Moose infrastructure.

Which is great. However we're still missing out on the introspectability of Moose - we can't ask Foo::Manual what its attributes are if we don't use the Moose meta-attribute system, and while most of the time the Moose generated accessors are as fast as hand rolled ones, Ovid does point out that by inlining a nasty regexp you can get faster than that, and sometimes you really do need to inline a nasty regexp (after checking your Devel::NYTProf output, obviously).

But wait! We can do that too - while normally we let Moose generate our accessors for us:

  package Foo::Moosey::Bar;

  use Moose::Role;

  has 'bar' => (is => 'rw');

which will create a 'bar' accessor automatically. However, if for whatever reason you need to provide your own, you can do that too:

  package Foo::Manual::Bar;

  use Moose::Role;

  has 'bar' => (
    is => 'rw',
    accessor => {
      bar => sub {
        my $self = shift;
        return $self->{bar} unless @_;
        $self->{bar} = $_[0];
      }
    }
  );

which now means that the metaclass attribute introspection will work, but the method contains exactly the code we supplied and therefore should run identically - and identically fast - to a pure hand-rolled solution.

Of course, I still need help with MooseX::Antlers to improve the compile time overhead, but basically there is, and was never, any excuse to not use Moose due to run time overhead - the trick is to use all the facilities of Moose to get the rest of your app running sooner, then profile it, then optimise the specific parts that show up in your profile. Better still, with this approach, you end up with the pretty, clear version still there so your optimised variant should make much more sense to the next maintenance programmer to boot.

Which really only leaves one important question:

What is the sound of half a Moose clapping?

Answers on a postcard to the company address at the bottom of this webpage ...

-- mst, out.