Copyright Notice

This text is copyright by CMP Media, LLC, and is used with their permission. Further distribution or use is not permitted.

This text has appeared in an edited form in SysAdmin/PerformanceComputing/UnixReview magazine. However, the version you are reading here is as the author originally submitted the article for publication, not after their editors applied their creativity.

Please read all the information in the table of contents before using this article.

Unix Review Column 13 (March 1997)

Objects, objects, objects! The programming world seems to be going ``object-oriented''. And Perl is no exception -- it too, has objects for the most recent 30% of its public life. However, unlike some other popular programming languages, you can write Perl code using objects, or avoid them entirely. Let's take a look at Perl's object system.

A Perl ``object'' is nothing more than a reference to a data structure. (I've used references in a number of the previous columns, if you need a refresher, or see ``man perlref'' on your system.) However, the reference has been ``blessed'' into a particular package, making this package the ``class'' of the object.

The purpose of the blessing is to allow ``methods'' to be called later on this object. A method is nothing more than an ordinary subroutine, but the subroutine is found within the package from which the object was blessed.

Let's take a look at a simple example. I'm going to create an object class called Car. Within it, I'm going to put a method that creates new cars for me to use, called ``new''. The invocation looks like this:

        $myCar = new Car;

Now, in order for this to work, we'll need to have a subroutine called ``new'' in the package Car. It'll look like this:

        sub Car::new {
                my $class = shift;
                my $self = {};
                bless $self, $class;
                $self->{passengers} = {};
                $self;
        }

Now, there's a lot of things going on here. So, one at a time:

  1. The subroutine is in the Car package, named ``new''. There's nothing special about the name ``new'', but it makes C++ and Smalltalk users feel like they know what's going on.

  2. The first parameter passed into this subroutine is the class name (that is, the package name). This subroutine shifts the parameter off into a local variable called $class. This is one of the things that distinguishes a method invocation from an ordinary subroutine invocation: there's always an extra first parameter.

  3. The ``object'' is created using a local variable $self. This variable is initialized to an empty anonymous hash. This is a fairly common practice, because instance variables (variables that are unique to each object) are then just elements of this hash.

  4. The object is ``blessed'' into this package. This doesn't change the value of $self, but the anonymous hash will now ``remember'' that it came from this package. This ``memory'' allows methods to be found in the right package later.

  5. An instance variable called ``passengers'' is created as an empty anonymous hash (reference) inside $self.

  6. Finally, the value of $self is returned.

Note that the return value is thus a reference to an anonymous hash, but this hash has been blessed into the Car package. You could thus use the variable $myCar (defined earlier) just like any hashref. However, objects work best when we treat them as ``black boxes'', instead interfacing with the object through ``instance methods''.

Let's look at an instance method on this car. Let's put ``Fred'' and ``Wilma'' into the car:

        enter $myCar qw(Fred Wilma);

The difference here is that instead of a classname after the method name, we now have an instance name. Also notice that we have a parameter passed to the method following the instance name. Let's see what that looks like inside the subroutine's definition:

        sub Car::enter {
                my $self = shift;
                foreach $_ (@_) {
                        $self->{passengers}->{$_} = "seat";
                }
                $self;
        }

Once again, a number of things are happening here. Let's walk through this one:

  1. Like the class method invocation of ``new'', there's an extra first parameter here. In this case, it's the instance (like $myCar). The first step of this subroutine shifts that instance off into a variable called $self. This will be the same $self as was defined in the ``new'' subroutine.

  2. Once the value for $self has been shifted off, the remaining values of @_ are now the people that we wish to put into the car. The foreach loop sets elements within the ``passengers'' instance variable (which is in turn referenced as an element in the hash for $self). The key will be the name of the passenger, and the corresponding value is just ``seat'', showing that they have a seat (a later version of this subroutine will perhaps record the choice of seats, but let's keep it simple for now).

  3. Although the return value of this method is really the side effect of having changed $self, we return $self once again, for reasons that will become obvious below.

So, this method causes the ``passengers'' instance variable to include the people listed as arguments to the subroutine.

Sometimes, the invocation syntax is better written using arrow notation, which looks like this:

        $myCar = Car->new;
        $myCar->enter(qw(Fred Barney));

In fact, because the return value of the enter method is the object again, we can combine this:

        $myCar = Car->new->enter(qw(Fred Barney));

This is the advantage of the arrow notation. We can also write this using the other (``indirect object'') notation as:

        $myCar = enter {new Car} qw(Fred Barney);

but as you can see, the parameters start to move further and further from the method name.

Once the passengers have been added, we'll need to see who they are. That's handled with an additional instance method:

        @passengers = passengers $myCar;
        print "passengers for $myCar is @passengers\n";

which results in something like:

        passengers for Car=HASH(0xb126c) is Barney Fred

Note here the value of $myCar when interpreted as a string. It's really just a debugging number, in the form of:

        Class=UNDERLYINGTYPE(0xHexAddress)

Also note that the user of the Car class does not need to know that we have stored the passengers as a hash... if we find it more efficient later to change that to a B-tree or a sorted list, we can do so, as long as the interfaces remain the same. That's why it's important to treat the object as a black box.

Here's one possible definition for the ``passengers'' method:

        sub Car::passengers {
                my $self = shift;
                sort keys %{$self->{passengers}};
        }

Once again, the first parameter is the object itself (like $myCar). This value is then accessed for the hash at the instance variable ``passengers''. The keys() operator is called on this hash, and the result is sorted ASCIIbetically.

Let's add one more method for completeness -- a way to remove the passengers that have been added. That'd look like this:

        sub Car::leave {
                my $self = shift;
                for (@_) {
                        delete $self->{passengers}->{$_};
                }
                $self;
        }

Now, let's mix them all together in a typical usage:

        %cars = (
                Flintstone =>
                        Car->new->enter(qw(Fred Wilma)),
                Rubble =>
                        Car->new->enter(qw(Barney Betty)),
        );
        ## show who is where
        foreach $family (sort keys %cars) {
                my @passengers = $cars{$family}->passengers;
                print "the $family car contains @passengers\n";
        }

Here, we set up two family cars in a hash called %cars. The key of the hash is the family name, and the corresponding value will be a particular car (of class Car). Then, there's a little routine that dumps out the current occupants of the car.

This then lets us perform typical transactions, such as Wilma jumping out from one car and into another:

        ## wilma decides it is better to ride with betty
        $cars{Flintstone}->leave("Wilma");
        $cars{Rubble}->enter("Wilma");

Hmm. Let's wrap this typical operation up into a method, and illustrate named parameters at the same time.

        sub Car::jump {
                my $self = shift;
                my %parms = @_;
                my $dest_car = $parms{TO} || Car->new;
                my $people = $parms{PEOPLE} || [$self->passengers];
                $people = [$people] unless ref $people;
                $self->leave(@$people);
                $dest_car->enter(@$people);
        }

And that allows us to say things like:

        ## wilma goes to the rubble car
        $cars{Flintstone}->jump(
                PEOPLE => qw(Wilma),
                TO => $cars{Rubble},
        );
        ## but it breaks down, so they all go to the other car
        $cars{Rubble}->jump(
                TO => $cars{Flintstone},
        );
        ## and then that breaks down, so they all get in a new car
        $newCar = $cars{Flintstone}->jump;
        @passengers = $newCar->passengers;
        print "new car contains @passengers\n";

Try these examples out. There's a lot to experment with here.

As you can see, objects allow us to define a new data type (a car) and operations on that type. In a future column, I'll illustrate other object things, like inheritance. Enjoy!


Randal L. Schwartz is a renowned expert on the Perl programming language (the lifeblood of the Internet), having contributed to a dozen top-selling books on the subject, and over 200 magazine articles. Schwartz runs a Perl training and consulting company (Stonehenge Consulting Services, Inc of Portland, Oregon), and is a highly sought-after speaker for his masterful stage combination of technical skill, comedic timing, and crowd rapport. And he's a pretty good Karaoke singer, winning contests regularly.

Schwartz can be reached for comment at merlyn@stonehenge.com or +1 503 777-0095, and welcomes questions on Perl and other related topics.