Building Simple, Useful Object-Programs with Perl

The Perl language was not originally designed to do object-oriented programming. In its design evolution a set of mechanisms has been retrofitted to give it some objective-features. Both Ruby and Perl are capable of solving programming problems spanning a wide berth of complexity. However, when a project becomes large enough (say over 1000 lines of code), basic OO-principles like modularization, encapsulation and Separation of Concerns enter the picture.

Software development is notoriously difficult to plan and map out accurately. Since you never really know how complex a project will end up being, requirements can change over time. You have to be confident the tools you're using will scale up to unknown tasks in later stages of development. The most Perl I've seen in one file was 18,000 lines of procedural code. This was someone else's work, but it is not unusual to come across Perl projects with tens of modules in them, and I have worked with one or two medium-sized projects like this. Needless to say, most Perl projects are somewhat small because people don't generally like to deal with tons of Perl.

But sometimes you have to do The Big Porgram Thing, and being able to divide code into meaningful modules or objects and interfaces is a valuable asset for organizing your code and maintaining sanity.

Truth be told, OO-Perl involves a lot of scaffolding, like 99%. There are over 10 uniquely different and nasty ways to make Perl "do" objects with progressive levels of sophistication. If you have read Damian Conway's book: Object-Oriented Perl, and surfed the manpages you should have a pretty good idea of what kind of treat you have in store for you.

The syntax of the scaffolding is not straightforward, and bears considerable explanation. You therefore must be a lover of books. Fortunately, you only have to "do" the scaffolding once, and can re-use it in your project's classes. Keeping to a simplified definition of Objects and classes makes for an easier time. If you omit some of the more subtley-corrupting ideas of OO-programming, such as classical inheritance, type-casting, exceptions, method overrides, etc. you can still use encapsulation, has-a relationships, simple design patterns, abstract interfaces and use Separation of Concerns and other principles to your advantage without too many problems.

Perl can be incredibly useful for fairly large projects if you are selective about which 00-approaches to adopt. Note that in most cases, useing a language like Ruby for the same thing will be much easier to read and write, and much cleaner. It's been my experience that they both take about the same time to write equivalent code, but Perl involves a lot of back-reading and document consulting to make the right scaffolding choices.

I mentioned earlier that Perl has many ways to implement objects and classes. This turns out not to be all that good a thing for consistency's sake, so you should stick with your choices. As the complexity of scaffolding increases to accomodate this OO-feature or that, the backreading and concept-bloat increases, similar in concpetual difficulty to some of the under-the-hood details of the Scala language. With Perl building capacity into sophisticated objects can be like weaving an elaborate lie; conflicts and inconsistencies creep into your tale as it becomes more complex.

First, on a scale of 1-10 of Perl object-oriented sophistication, the following driver file and class file rate 2/10: simple. It is a HelloWorld program. Each piece of code resides in a separate file in the same directory. You run the driver from the command line, and it calls into the class to do the dirty-work. This is basic file-level encapsulation. If you're not doing this in any OO-language, you are being a slob.

HelloWorld in 10 lines of code and 2 files

The Driver (you can name it anything)

# Driver

use HelloWorld;
my $obj = HelloWorld->new();
print $obj->message();

The Class (must be named HelloWorld.pm)

# OO Class Module

package HelloWorld;

sub new
   {
   bless { msg => "hello world" }, shift;
   }

sub message
   {
   my $a = shift;
   return $a->{msg};
   }

1;

Explanation. The driver has a use HelloWorld; statement which imports the HelloWorld class file so it can be accessed.

The my $obj = HelloWorld->new(); line is a call to the object's constructor subroutine: named new(). This is a convention. The constructor can be named anything you like, but "new" suggests object creation and is self-documenting to many people. When this line is executed, an instance of the class is created (an object) which is stored as a reference in local scalar variable $obj (in this case $obj holds a hash reference, explained later).

The last line in the driver calls a method in the object named message, which it then prints to standard output, and the program ends.

In order to understand the driver, you need to know about Perl's data types, what a reference to a data structure is, and about accessing information stored in data structure references with the '->' arrow-operator. 1

Ok, onto the second part of the explanation, the class file, HelloWorld.pm 2 3

It is here we encounter the first of a few oddities with Perl's object implementation, the Package declaration near the top of the file. Perl implements class definitions as unique namespaces. The package declaration is the class declaration. Since we have achieved some separation at the file level, this namespace applies only to the entire file, HelloWorld.pm. Note the namespace matches the filename. Also note the entire file is also a bona-fide Perl module, and ends with a 1 to return true to things that evaluate it. Note also the filename ends in .pm, indicating it is a (P)erl (M)odule. You don't run code directly in modules. You include modules in other code, and make calls from from there. So, this module is not a standalone program. It is a class that uses the library idiom. No Export or other directives are required as with regular Perl modules. Object-oriented code doesn't need that particular scaffolding.

The next block of code (the new() sub) is the constructor. It is named new by convention so people understand this function serves as the constructor. It is the only entrypoint to this module, and the only class-level method that should be run by calling code from the outside.

Inside the constructor we have one line of code to initialize an object. It is a call to the bless() function. This is the second oddity of Perl objects. Bless makes an object out of the class namespace by blessing a reference (usually to a data structure) into the namespace. The data structure in this case is a hash reference holding a key/value pair. The key/value pair represents a settable property or attribute of the object and its value. Once bless blesses the structure into the namespace, that structure and all the instance methods listed in the file become part of the object reference it returns. Its return value ends the construction process, so it's the last step in the constructor. In the two-argument form, bless takes two arguments, the first being a reference, and the second being the name of the class.

When a line of code calls a method in Perl-OO, the name of the class it's calling the method on is included as a string in the first parameter to the function being called. Parameters are always passed as arrays to functions in Perl, so the name of the targeted class or object is always @_[0]. The rest of the parameters (if any) follow in subsequent indexes of the array starting at @_[1]. This also applies to the constructor, which is just a function that recieves parameters. This is why shift is used here. It pops-off the first item in the implicit argument array, which is always @_[0]. This is the string HelloWorld, from HelloWorld->new() which is what appears to the left of the arrow-operator in the second line of the driver. In this case, HelloWorld's new() is what is being called, so the new function that recieves it knows how the name of the class is being expressed. More Perl oddity.

The last code block in the class is sub message() which is simply an instance method to return the contents of the msg attribute defined in the constructor. It is called via the intialized object.

Hey Let's get more complex scaffolding in place

To make a practical starting template for classes, we will resort to adding in more sophistication. This class hits about a 4 or a 5 on the sophistication meter, and can be used as a cut-and-paste template for real projects. It has a few more OO-features and extra robustness for common cases of use.

Keeper class: (Keeper.pm)

package Keeper;

use strict;

   {
   my $_count = 0;
   sub _incr_count { ++$_count }
   sub _decr_count { --$_count }
   sub get_count   { $_count }
   }


sub new
   {
   my ($self,$args) = @_;

   my $class = ref ($self) || $self;

   my $properties =
      {
      _name   => $args->{name}   || "<unknown>",
      _gender => $args->{gender} || "<unknown>",
      };

   my $instance = bless $properties, $class;
   $class->_incr_count;

   return $instance;
   }


sub DESTROY
   {
   my $class = shift;
   $class->_decr_count;
   }


sub name
   {
   my ($obj,$arg) = @_;
   if ($arg) { $obj->{_name} = $arg }
   $obj->{_name};
   }


sub gender
   {
   my ($obj,$arg) = @_;
   if ($arg) { $obj->{_gender} = $arg; }
   $obj->{_gender};
   }

1;

Driver for Keeper class:

require Zoo::Keeper;


MAIN:
   {
   my $zookeeper_1 = Keeper->new( {name=>"Fred",gender=>"Male"} );
   inspect ($zookeeper_1);

   $zookeeper_1->name("Thomas");
   inspect ($zookeeper_1,"\n[Changing name]\n");

   my $zookeeper_2 = Keeper->new( {name=>"Selma",gender=>"Female"} );
   inspect ($zookeeper_2);

   my $zookeeper_3 = Keeper->new( {gender=>"Female"} );
   inspect ($zookeeper_3);
   }


sub inspect
   {
   my ($obj,$msg) = @_;
   print $msg || "\n[Creating object]\n";
   print "Object: ", $obj ? "allocated" : "not allaocated","\n";
   print "Name: ", $obj->name,"\n";
   print "Gender: ". $obj->gender,"\n";
   print "Count: ", $obj->get_count, "\n";
   }

The intended directory structure for this pair of files is to name the driver anything you want, and store the driver in a top-level project directory. Below this directory, make a subdirectory called Zoo, and place the class file in there, naming it Keeper.pm. This sets a small example on how to address classes for import in a directory structure by using the require Zoo::Keeper to access Keeper.pm inside the Zoo subdirectory.

The main body of the driver is wrapped in a closure and labeled MAIN:. This is simply for visual organization and documentation. The MAIN function in the driver creates three zookeeper objects with different name and gender properties. It also changes one of the zookeeper names to exercise the setting option on one of the properties. It makes use of a small diagnostic output function sub inspect to printout what's going on with the objects.

The class file itself uses use strict; as a robustness feature, which is followed by a closure containing class-level members. This is how Perl defines class methods and properties, inside closures. In this case a simple classwide object counter is implemented as a class property, and three accessors are used to increment, decrement and fetch the counter respectively.

The constructor handles multiple initialization arguments, but sticks to the common practice of passing in properties as a single hash reference, which has the feature of being self-documenting. Two properties name and gender are expected to be supplied by the calling code, using the keys name and gender and the passed values are stored in a property hash if the keys match. If one or all properties are not set, the constructor supplies the default value of <unknown>. This is a robustness feature.

The two-argument form of bless() is used to allow for inheritance via the @isa array, but the class is not otherwise structured to handle inheritance in any way. This should not be done unless you have a full reading of Damian Conway's book to understand the pitfalls of using inheritance in OO-Perl.

The constructor increments the class object counter at object creation time, and the sub DESTROY method decrements the counter if one of the objects is destroyed. This is mainly for show, but should function and is logically required to maintain this type of class variable.

Read/write accessors for name and gender are supplied, and make a determination on whether the calling function is setting or getting the value, responding accordingly. The value is always returned irrespective of getting or setting.

Most of these idioms and forms come from Damian Conway's book, Object Orient Perl, but a lot of my own stuff is added in. This is something I would probably use as a starting point at work, if I had to build a reasonably sized Perl project.

Below, I've included the rough Ruby equivalent of the above class and driver but won't explain it, except by making the general observation that it took me just about as long to write, but Ruby syntax is so crisp, with almost no scaffolding that I just did coding and hardly any reading on syntactical oddities and workarounds. With Perl, alas, I spent a majority of my time reading and trying to remember particular oddities of the language in addition to writing / debugging code.

So if you want my honest opinion, don't get seduced by CPAN or nice people who are enthusiasts of a terrible solution. Take it from me: go with Ruby if you any choice whatsoever. And I mean this advice to be taken in all seriousness of heart. Perl has had it. Although still quite useful, it's seen its heyday. There are better scripting languages nowadays, (Ruby may be the best) which don't have the shameful, hackish awfulness of OO-Perl.

Ruby Equivalent Driver:

require_relative "Foo/Keeper.rb"

def show(keeper,*msg)
   if (msg.empty?)
     msg[0] = "[New object]"
   end
   puts "\n",msg[0]
   puts "Object " + keeper.works
   puts "Count: " + Keeper.get_count
   puts "Name: " + keeper.name
   puts "Gender: " + keeper.gender
end

keeper_1 = Keeper.new("Fred","Male")
show(keeper_1)

keeper_1.name = "Tim"
show(keeper_1 , "[Name change]")

keeper_2 = Keeper.new("Selma","Female")
show(keeper_2)

keeper_3 = Keeper.new("Mary","Female")
show(keeper_3)

Ruby Equivalent Class File:

class Keeper

   @@count = 0

   def Keeper.incr_count
      @@count += 1
   end

   def Keeper.decr_count
      @@count -= 1
   end

   def Keeper.get_count
      @@count.to_s
   end

   attr_accessor :name, :gender

   def initialize(name="unknown", gender="unknown")
      @name = name
      @gender = gender
      Keeper.incr_count
   end

   def works
      "works"
   end

end


  1. (1) To get to this level of knowledge, you'll need to read the book (cover to cover): Learning Perl by Randal L. Schwartz, in my opinion the best technical book ever written for the uninitiated. You will also need to read Chapter 2 - Essential Perl of Damian Conway's book Object Oriented Perl to understand what references are. Fortunately, Perl authors and the community as a whole has a sense of humor, so it's a lot more fun to learn than it sounds. 

  2. (2) A class is a blueprint which defines variables and functions closely (topically) related to one another in a package. An object is a live, usable copy of that package. In a program, you generally interact with objects. The code itself is defined by the class and does not change. 

  3. (3) A basic class is like a blueprint. It is inactive, and just defines code. Only one function can be called directly on it from the outside: the constructor. The constructor, in this case is the new function. When outside code calls the constructor it is like a call to manufacture a working copy of the class from the blueprint. This working copy of the class is called an object. A fresh object is returned to the calling code by the constructor, and assigned to a variable so it can be interacted-with later. The variable in this case is the $obj variable, and is located in the driver.