A gentle introduction to Moose

user-pic

Wiki Extras for this post

Perl has had Object Oriented features since the release in Perl 5 in 1994. The Perl 5 OO is somewhat of a roll-your-own variety however, in that in classic Perl style, it provides the minimum in-language support required and leaves you to handle the rest.

Perl's roll-your-own OO feature has a major benefit in that there is very little dictated by the language about how you do things. It also has the drawback of being somewhat daunting for the newcomer, but for better or worse, it has become familiar.

Another benefit is that the feature has been free to evolve, and since OO is not dictated by the language some very smart developers have been free to explore ways of doing things that might not otherwise be thought of. Moose is the product of that exploration and is rapidly becoming the de-facto standard for OO in Perl. Today we'll explore the basics of creating and using objects in Moose.

In conventional Perl object oriented programming, there are three main things you need to do. Those are:

  1. Object Creation
  2. Attributes and Accessors
  3. Subclassing

We'll take a quick run around each of these and compare the stock-Perl way and the Moose way.

Object Creation

In stock Perl, the way you create an object is to create a variable and then 'bless' a reference to it into a class. You can do this with any variable type, but most often this is done with a hash reference. This is usually done in a 'new' subroutine:

   package FooClass;

sub new { my $class = shift; my $foo = {}; bless $foo, 'FooClass'; return $foo; }

This provides you with an object. It provides little else, however. The Moose way of doing this is somewhat simpler:

    package FooClass;
    use Moose;
 

That's it. Moose creates the new() routine for you. If your class has attributes, Moose also takes care of handling populating them based on any arguments you might pass.

Creating an instance of a class follows the same convention in a Moose object as a stock Perl Object in that you simply call new() on the class. Moose, however, let's you pass initial values for your attributes in the new call. For example:

    FooClass->new( name => 'bob' );
 

That's a convenient bit of useful 'free' functionality you get with Moose.

Attributes and Accessors

In stock Perl, your object is (often) a hash and your attributes are simply members of that hash. Often, the attributes are accessed directly:

 
    $foo->{name};
 

This, however, is generally frowned upon because it provides very little structure and makes it very easy to accidentally mis-declare attributes. $foo->{naem} can be a very hard bug to find. The generally accepted 'best practice' is to create accessor subroutines for your variables, this limits those using your object to work with the attributes you created. (The OO geeks refer to this as 'encapsulation'). The problem is that in stock Perl OO you need to create these yourself:

    sub name {
        my $self = shift;
        if( @_ ) {
            $self->{'name'} = $_[0];
        }
        return $self->{'name'};
    }
 

This is a lot of code just to get/set attributes of an object. In true Perl form, a CPAN module sprung up to make this easier. Class::Accessor makes accessors a lot easier:

    package FooClass;
    use base qw(Class::Accessor);

FooClass->mk_accessors(qw(name age));

Class::Accessor creates the new method for you. This is clearly better than doing it by hand. It's now basically impossible to accidentally create attributes or access the wrong ones. There is one thing it doesn't do though. It doesn't prevent you in any way from putting junk in the attributes. This is perfectly legal:

   $foo = FooClass->new();
   $foo->name(192);
   $foo->age('cupcake');
 

Moose, on the other hand, does not only what Class::Accessor does, but adds type checking (and some other goodies). The same object created with Moose:

    package FooClass;
    use Moose;

has 'name' => (
    is => 'rw',
    isa => 'Str'
);

has 'age' => (
    is => 'rw',
    isa => 'Int'
);
1;

The above provides the same name() and age() we are used to. But if we try to set age to 'cupcake' now we will get an error. We also get the ability to say, essentially 'this attribute can not be changed after the object is created.' (Among other things, The moose attribute and type checking system is amazingly flexible and warrants it's own article)

Subclassing

Subclassing in OO is designed to allow specialization of a particular class. The classic example is that you can have a Shape class that can provide a certain amount of functionality, and when you need a Square you can subclass Shape and add only the code that makes Square unique. In stock Perl OO you would do this:

    package Square;
    @ISA = ("Shape");

# the rest of your Square class

Perl has the smarts to search the classes defined in @ISA for methods if it can't find them in the Square class. It works well, but it is somewhat non-intuitive.

Moose has subclassing too, and it makes particularly clear what you are doing:

    package Square;
    use Moose;
    extends 'Shape';

# the rest of your Square Class

Pretty straightforward.

Other Moose-y things

Moose doesn't do anything you can't already do with Perl OO. Moose is built on the same OO features supported in stock Perl. It does, however, take care of a huge amount of the work you'd otherwise have to do yourself, freeing you up to work on your 'new' functionality rather than reinventing the wheel yet again.

In this article I've showed you how to do the 'normal' Perl OO things in Moose. At this point you know how to do basic OO programming using Moose and could start replacing your hand-rolled Perl OO classes with ones built with Moose.

Moose, however, is more than just a bit of clever syntax and some time savings. Moose has some additional features that make it really amazing to work with. I will only touch on them here as they would require an entire book to cover properly. Some of the features available using Moose are:

  • Class Metadata - the ability to have your code examine the structure of your objects
  • Coercion - the ability to convert types of one type to another when necessary
  • Method Modifiers - the ability to add code that runs before, after or around an existing method
  • Roles - The ability to add pre-defined functionality to classes without subclassing

There are many others that are worth investigating as well. Roles are particularly interesting and provide flexibility that is unheard of in almost any object oriented language. Also worth noting are the numerous Moose extensions that provide even more functionality beyond standard Moose.

To learn more about Moose, check out the fine manual. For a bit more detail on Moose vs. Stock Perl OO, check out the unsweetened portion of the manual.

You can also check out the Moose homepage. Finally, I've started a 'Moose gotchas' list on the Catalyzed Wiki to track some of the trickier bits of Moose I've encountered. Feel free to add your own as you discover the joys of Moose.

No TrackBacks

TrackBack URL: http://www.catalyzed.org/mt/mt-tb.fcgi/46

5 Comments

| Leave a comment

This is an excellent introduction to Moose, Jay. You've covered the bare necessities well and are spot on in handling the essentials of this interesting module. Look very much forward to the following lessons, e.g. moose attribute and type checking system ;)

Thanks, nice article, looking forward for your newbie friendly intro to Moose.

Thanks for writing this - I've been meaning to look into Moose for a while now, and a gentle introduction is just the thing to wet the appetite.

I wonder one thing though: Did you run your examples? I couldn't get FooClass to work without changing the syntax a bit (curly braces to parens, adding semicolons, and the traditional "1;":

- the curly braces vs. parens had me stumped for a number of seconds, so I thought I'd let you know.)

Thanks again for the article! Adam

Thanks for the catch, Adam. I make the same mistake in my own Moose classes over and over. Seems there's a part of me that is not ready to let go of the hashref business in Perl OO. :-)

It's corrected. Thanks again.

You're welcome! We all make typos, but especially in an introduction they lead to the, often, humble reader thinking "Hmm, why don't I get this?", so it's great that you fixed it :-)

Leave a comment

Sponsored By


Ionzero: Rescue your dev project.
OpenID accepted here Learn more about OpenID