Understanding Object-Oriented Perl

Have you ever wondered what it is you’re doing when you write object-oriented Perl? What is the @ISA array for, and what on earth does bless do? About a week ago I was sitting at BW3 with several members of the Grand Rapids Java Users Group who had those same questions. Read on for what I hope are relatively simple answers to those questions and a few others.

Class declaration

Creating a new class in Perl is simple: just declare a package (or a namespace, if you prefer that term) and start adding methods in that package. For example:

package My::Animal;
sub new {
    # ...
}
sub sound {
    # ...
}
1;

Place this code in a file called Animal.pm in the My/ directory and, as long as the parent directory of My/ is in Perl’s include path, it is easy to include this class in other Perl programs:

use My::Animal;
my $animal = My::Animal->new;

Object creation

If you’ve written any object-oriented Perl code, you’re probably familiar with this basic constructor:

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self = {};
    bless $self, $class;
    return $self;
}

First, let me be clear about the name new: it is not special. You could just as easily name your constructor create or build. Again, there is nothing special about the name new. What is special about the constructor is what it does, so let’s step through it line by line.

my $proto = shift;

One of the invisible things happening here involves a special Perl variable often referred to as the default list: @_. We call it the default list because operators like shift and grep operate on this list by default if no other list is given. So, this line could be rewritten as: my $proto = shift @_; and it would mean the same thing.

In the context of a subroutine (or method — they’re basically the same thing) this default array contains the parameters passed in by the caller. When you call a method on an object or class, the first parameter is always the object’s reference or the class name, depending on how it is called. So what we’re doing here is assigning this first parameter to the lexically scoped variable $proto. So now $proto contains either the object that this method was called on or the name of the class.

my $class = ref $proto || $proto;

Because $proto might be either the object (a reference) or the class name (a string value), we have to do something to reliably get the class name for use later. The ref operator takes an object and returns the class name for that object or nothing if it was not given an object. So, what we’re saying here is, “Give me the class of this object or, if it was not an object, assume $proto was the class name to begin with.”

my $self = {};

Create a reference to a hash. People often use this hash as a place to store instance variables because it will be passed to all method invocations for this object.

bless $self, $class;

The bless function takes two arguments: a reference and a class name. What it does is tell Perl that the given reference is actually an instance of a specific class so that, when you call a method on your object, it knows where to start looking for the method definitions.

return $self;

Return our newly created and blessed object.

Calling object and class methods

I’m assume that you are familiar enough with object-oriented programming to understand the difference between an object method and a class method…

Class methods

These are all ways to call a class method:

$dog = My::Animal->new(@args) # my preference;
# Or, using indirect object syntax
$dog = new My::Animal @args;
$dog = new My::Animal(@args);

Class methods receive a list of arguments in @_ (the default list — remember?). The first of those arguments is the name of the class the method was called on. Going back to our constructor, the class name is stored in $proto and then in $class when the ref function returns nothing.

Object methods

These are all ways to call a method on a specific object:

$sound = $dog->sound(@args);
$sound = sound $dog @args; # Indirect object syntax (discouraged)

Object methods also receive a list of arguments in @_, though this time the first argument is the object that the method was called on. That is why, in most object method definitions, you will see the first line is something like my $self = shift;. The object, because I use a blessed hash reference, can be used to store and retrieve our instance data during object method invocations. For example:

package My::Animal;
sub new {
    my $proto = shift;
    my $name = shift;
    my $class = ref $proto || $proto;
    my $self = {
        name => $name,
    };
    bless $self, $class;
    return $self;
}
sub name {
    my $self = shift;
    print "Hello, my name is $self->{name}n";
}

Note that indirect object syntax is discouraged when calling object methods due to potentially ambiguous syntax, and possibly other issues I’m not aware of. I prefer the first invocation method using the arrow operator for both object and class methods.

Inheritance

Inheritance in Perl works a lot differently than any other language I’ve ever used (Java, Ruby, Python, C++). Let’s set up a simple example to work through.

package Animal;
sub new { ... }
sub live { print "I'm alive!n"; }
sub speak { print "Animals can't speak.n"; }

package Dog;
use Animal;
use vars qw(@ISA);
@ISA = qw(Animal);
sub speak { print "BARK!n"; }

package Beagle;
use base qw(Dog);

So, we have a base class Animal that provides a constructor called new, a Dog class that inherits from Animal, and a Beagle class that inherits from Dog. As you can probably guess by looking at the Dog class, the @ISA array declares what classes we inherit from. That’s right: Perl supports multiple inheritance — more on that in a minute. You probably also noticed that there are two ways to set up inheritance.

Package Dog does things the older way by use-ing the package and then manually placing it into the @ISA array. This has some downsides, not the least of which is that it clobbers anything that happened to already be in the inheritance list. The preferred method is to use use base as demonstrated in the Beagle class.

Method resolution order

Like I said before, Perl does things its own way. Remember the bless function from our constructor? It tells Perl that a given reference belongs to a specific class. When a method is called on that reference, Perl starts with its class and looks for a method definition with that name. If it doesn’t find it, Perl checks the first class in the @ISA array. If not found there, it checks that class’ first @ISA array entry. Continuing this pattern, Perl performs a depth-first search until it finds a method with the correct name and executes it.

See Class::C3’s documentation on CPAN for more discussion on the topic of method resolution order.

In the end

Hopefully you’re feeling a bit more confortable with OO Perl now and understand some of the unique pieces that make this language what it is. If you’re still craving more information, try the following sources:

One Comment

  1. Deva
    Posted March 3, 2008 at 2:46 am | Permalink

    Good material to start with. Thanks a lot.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*