Wednesday, December 16, 2009

Ironman FAIL

Oops... I moved back to Chamonix over the weekend and completely forgot about blogging.

I guess I'll take a few days to get settled in and then start writing again. I'm aiming for chartreuse with alternating red and monkeyshit highlights and a fishnet, but unfortunately mst has been blogging much more consistently than me so far.

Friday, December 4, 2009

Simplifying BEGIN { } with Moose roles

This is a common Perl pattern:

package MyClass;
use Moose

use Try::Tiny;

use namespace::autoclean;

BEGIN {
    if ( try { require Foo; 1 } ) {
        *bar = sub {
            my $self = shift;
            Foo::foo($self->baz);
        };
    } else {
        *bar = sub {
            ... # fallback implementation
        };
    }
}

However, since this is a Moose class there is another way:

package MyClass;
use Moose

use Try::Tiny;

use namespace::autoclean;

with try { require Foo; 1 }
    ? "MyClass::Bar::Foo"
    : "MyClass::Bar::Fallback";
package MyClass::Foo;
use Moose::Role;

use Foo qw(foo);

use namespace::autoclean;

sub bar {
    my $self = shift;

    foo($self->baz);
}
package MyClass::Bar::Fallback;
use Moose::Role;

use namespace::autoclean;

sub bar {
    ...; # fallback implementation
}

Obviously for something that simple it doesn't make sense, but if there is more than one method involved, or the fallback implementation is a little long, it really helps readability in my opinion. Going one step further, you can create an abstract role like this:

package MyClass::Bar::API;
use Moose::Role;

use namespace::autoclean;

requires "bar";

and add it to the class's with statement to validate that all the required methods are really provided by one of the roles.

Role inclusion is usually thought of as something very static, but dynamism can be very handy without doesn't hurting the structure of the code.

If you want to be pedantic the role inclusion is not at compile time, but the loading of Foo is done at compile time inside the role (Foo is usually why it was in a BEGIN block in the first place, in most of the code I've seen).