woman using a laptop with her daughter

Inspired by my parents coming to visit at the end of the week, I thought I’d write about how Perl classes can have parents” as well, from which they inherit methods. Although it might seem on the surface as though there’s more than one way to do it, these techniques all share the same underlying mechanism.

Where it all BEGINs: @ISA

Perl classes are just repurposed packages, i.e., a namespace for variables and subroutines. The two key differences are:

If you wanted to do everything by hand at the lowest level, you could make a subclass at compile time like this:

package Local::MyChildClass;
BEGIN { # don't do this:
    require Local::MyParentClass;
    push @ISA, 'Local::MyParentClass';
}

Don’t do that though, because we have…

base and parent

In 1997 Perl 5.004_04 introduced the base pragma (back when Perl used that kind of versioning scheme; in these days of semantic versioning we’d call it version 5.4.4). It does the above BEGIN block in a single line:

use base 'Local::MyParentClass'; # don't do this unless you're also using fields

You might see use base in older code especially if it’s also using the fields pragma. However, Perl developers discourage both as the former silences certain module loading errors while the latter is at odds with the object-​oriented programming principle of encapsulation.

So use parent instead, which Perl has included since version 5.10.1 in 2009:

use parent 'Local::MyParentClass';

A couple of years ago my Newfold Digital colleague David Oswald created a fork of parent called parent::versioned that supports specifying the lowest version for superclasses. You call it like this:

use parent::versioned ['Local::MyParentClass' => 1.23];

Within an OO system

There are dozens of object-​oriented programming systems on CPAN that provide syntactic sugar and extra features to Perl’s minimal but flexible basics. Two of the more popular ones, Moose and Moo, offer an extends keyword that you should use instead of use parent so that your subclasses may take advantage of their features:

package Local::MyChildClass;
use Moo;
extends 'Local::MyParentClass';

Moose can also specify a required superclass version:

package Local::MyChildClass;
use Moose;
extends 'Local::MyParentClass' => {-version => 1.23};

Also, use the MooseX::NonMoose module when extending non-​Moose classes, again so you get Moose features even though your methods are coming from somewhere else:

package Local::MyMooseClass;
use Moose;
use MooseX::NonMoose;
extends 'Local::MyPlainParentClass';

The experimental Object::Pad module specifies a single superclass while defining the class name with an optional version. Per the author’s suggested file layout, including a required minimum version, it would look like:

use Object::Pad 0.41;
package Local::MyChildClass;
class Local::MyChildClass isa Local::MyParentClass 1.23;

Object::Pad and Corinna, its inspiration, are works in progress so this syntax isn’t set in stone. The latter’s designer Curtis Ovid” Poe blogged earlier this week about considering a more self-​consistent syntax.

Multiple inheritance vs. roles

To quote the Perl documentation, multiple inheritance often indicates a design problem, but Perl always gives you enough rope to hang yourself with if you ask for it.” All the techniques described above except for Object::Pad support multiple inheritance by specifying a list of superclasses. For example:

package Local::MyChildClass;
use parent qw(Local::MyParentClass1 Local::MyParentClass2);

If you’re using roles instead of or on top of superclasses (I’ve seen both situations) and your OO system doesn’t support them on its own, you can use the Role::Tiny module, first by describing your role in one package and then consuming it in another:

package Local::DoesSomething;
use Role::Tiny;

...

1;
package Local::MyConsumer;
use Role::Tiny::With;
with 'Local::DoesSomething';

...

1;

Moo::Role uses Role::Tiny under the hood and Moo can compose roles from either. The syntax for both Moo and Moose is similar:

package Local::DoesSomething;
use Moo::Role; # or "use Moose::Role;"

...

1;
package Local::MyConsumer;
use Moo; # or "use Moose;"
with 'Local::DoesSomething';

...

1;

Object::Pad specifies roles with the role keyword, and both classes and roles use does to consume them:

use Object::Pad 0.56;
package Local::DoesSomething;
role Local::DoesSomething does Local::DoesSomethingElse;

...

1;
use Object::Pad 0.56;
package Local::MyConsumer;
class Local::MyConsumer does Local::DoesSomething;

...

1;

The previous caveat about possible changes to this syntax applies.

Like parent, (sort of) like child

Of course, the whole point of inheritance or role consumption is so your child or consumer class can reuse functions and methods. Each of the techniques above has its ways of overriding that code, from the Perl built-​in SUPER pseudo-​class to Moose’s override and super keywords, to Moose’s and Moo’s method modifiers. (You can use the latter outside of Moo since it’s provided by Class::Method::Modifiers.)

I’ve written about choosing between overriding and modifying methods before, and when it comes to Moose and Moo code I’m now on the side of using the around method modifier if a method needs to call an inherited or consumed method of the same name. Object::Pad doesn’t have method modifiers (yet), so classes that use it will have to satisfy themselves with SUPER in their methods with an :override attribute that will throw an error if a parent doesn’t also provide the same method.

The Parent Wrap

In the end, your choice of Perl OO system will determine how (or whether) you handle inheritance and may even be a deciding factor. Which would you choose? And more importantly, have I made my parents proud with this post?

woman in black tank top and blue denim jeans

This blog has devoted a fair amount of attention to the popular and multifaceted object-​oriented system Moose and its lightweight subset Moo. I’ve also covered Object::Pad, the testbed of concepts and syntax for Corinna, the proposed next-​generation Perl core OO system. But what if your project is too memory‑, performance‑, or dependency-​constrained for these options?

It turns out that CPAN has a rich history of lighter-​weight OO modules to meet many different needs. If you can live with their trade-​offs, they’re worth investigating instead of rolling your own layer over Perl’s OO. Here are a few.

Class::Struct

Class::Structs main claim to fame is its inclusion in the standard Perl distribution, so there’s no need to install dependencies from CPAN. It provides a syntax for defining classes as C‑style structs at either compile time or runtime. (There’s no speed advantage to the former; it just means that your class will be built as if you had written the accessors yourself as subs.) Here’s an example:

#!/usr/bin/env perl

use v5.24; # for strict, say, and postfix dereferencing
use warnings;

package Local::MyClass;
use Class::Struct (
    foo => '$',
    bar => '@',
    baz => '%',
);

package main;

my $obj = Local::MyClass->new(
    foo => 'hello',
    bar => [1, 2, 3],
    baz => { name => 'Mark'},
);

say $obj->foo, ' ', $obj->baz('name');
say join ',', $obj->bar->@*;

# replace the name element of baz
$obj->baz(name => 'Sharon');

# replace the second element of bar
$obj->bar(1, 'replaced');
say $obj->foo, ' ', $obj->baz('name');
say join ',', $obj->bar->@*;

And here’s the output:

hello Mark
1,2,3
hello Sharon
1,replaced,3

Note that Class::Struct supports accessors for scalar, array, and hash types, as well as other classes (not demonstrated). Consult the module’s documentation for the different ways to define and retrieve them.

Class::Accessor

Class::Accessor does one thing: it makes accessors and mutators (also known as getters and setters) for fields in your class. Okay, it actually does another thing: it provides your class with a new method to initialize those fields. Those accessors can be read-​write, read-​only, or write-​only. (Why would you want write-​only accessors?) You can define any of them using either its historical class methods or a Moose-​like attribute syntax.

If you’re trying to squeeze every bit of performance out of your code and can sacrifice a little flexibility in altering accessor behavior, you can opt for Class::Accessor::Fast or Class::Accessor::Faster. The former still uses hash references under the hood to represent objects and the latter uses array references. The main Class::Accessor documentation contains an efficiency comparison of the three for your edification.

Here’s an example script using Class::Accessor::Faster and the Moose-​like syntax:

#!/usr/bin/env perl

use v5.12; # for strict and say
use warnings;

package Local::MyClass;
use Class::Accessor::Faster 'moose-like';

has readwrite => (is => 'rw');
has readonly  => (is => 'ro');

package main;

my $obj = Local::MyClass->new( { # must be a hash reference
    readwrite => 'hello',
    readonly  => 'world',
} );

say $obj->readwrite, ' ', $obj->readonly;
$obj->readwrite('greetings');
say $obj->readwrite, ' ', $obj->readonly;

# throws an error
$obj->readonly('Cleveland');

And here is its output:

hello world
greetings world
'main' cannot alter the value of 'readonly' on objects of class 'Local::MyClass' at ./caf.pl line 24.

Class::Tiny

Class::Tiny both does less and more than Class::Accessor. All of its generated accessors are read-​write, but you can also give their attributes lazy defaults. Its generated constructor takes arguments via either a Class::Accessor-style hash reference or a plain list of key/​value pairs, so that’s a little more convenient. It also supports Moose-​style BUILDARGS, BUILD, and DEMOLISH methods for argument adjustment, validation, and object cleanup, respectively.

It’s a toss-​up as to which of the previous two is better.” You’ll have to examine their respective features and determine which ones map to your needs.

Here’s an example script that shows a few of Class::Tiny’s unique features:

#!/usr/bin/env perl

use v5.12; # for strict and say
use warnings;

package Local::MyClass;
use Class::Tiny qw<foo bar>,
{
    baz       => 'default baz',
    timestamp => sub { time },
};

package main;

my $obj = Local::MyClass->new( # plain key-values OK
    foo => 'hello',
    bar => 'world',
);

say $obj->foo, ' ', $obj->bar;
say 'Object built on ', scalar localtime $obj->timestamp;
$obj->foo('greetings');
$obj->bar('Cleveland');
say $obj->foo, ' ', $obj->bar;
say $obj->baz;

And its output:

hello world
Object built on Tue Sep  7 09:00:00 2021
greetings Cleveland
default baz

Object::Tiny

For an even more minimalist approach, consider Object::Tiny. Its accessors are read-​only, it gives you a simple constructor, and that’s it. Its documentation lists a number of reasons why it can be superior to Class::Accessor, including lower memory usage and less typing. There’s also a fork called Object::Tiny::RW that adds read-​write support to its accessors.

Class::Tiny’s documentation contains a feature table comparison of it, Object::Tiny, and Class::Accessor. This may help you decide which to use.

Here’s an example script:

#!/usr/bin/env perl

use v5.12; # for strict and say
use warnings;

package Local::MyClass;
use Object::Tiny qw<foo bar>;

package main;

my $obj = Local::MyClass->new(
    foo => 'hello',
    bar => 'world',
);

say $obj->foo, ' ', $obj->bar;

# has no effect unless you use Object::Tiny::RW
$obj->foo('greetings');
say $obj->foo, ' ', $obj->bar;

And its output:

hello world
hello world

Add some speed with XS

If the above options are still too slow and you don’t mind requiring a C compiler to install them, there are variants that use Perl’s XS interface instead of pure Perl code:

Roles with Role::Tiny

If you’re eyeing Moose and Moo’s support for roles (also known as traits) as an alternative to inheritance but still want to keep things light with one of the above modules, you’re in luck. The Role::Tiny module lets you compose methods into consuming classes with Moo-​like syntax and will pull in Common Lisp Object System-style method modifier support from Class::Method::Modifiers if you need it. It does mean another couple of CPAN dependencies, so if that’s a problem in your situation you’ll just have to live without roles.

Here’s an example script with a role and a consuming class that uses Class::Tiny. The role requires that its consumers implement a required_method, provides a foo method that uses it, and a method modifier for bar.

#!/usr/bin/env perl

use v5.12; # for strict and say
use warnings;

package Local::MyRole;
use Role::Tiny;

requires 'required_method';

sub foo {
    my $self = shift;
    say $self->required_method();
}

before bar => sub {
    warn 'About to call bar...';
};

package Local::MyClass;
use Class::Tiny {name => ''};
use Role::Tiny::With;
with 'Local::MyRole';

sub bar {
    my ($self, $greeting) = @_;
    say "$greeting ", $self->name;
}

sub required_method {
    my $self = shift;
    return 'Required by Local::MyRole';
}

package main;

my $obj = Local::MyClass->new(name => 'Mark');
$obj->bar('hello');

$obj->name('Sharon');
$obj->bar('salutations');

$obj->foo();

And its output:

About to call bar... at ./rt.pl line 17.
hello Mark
About to call bar... at ./rt.pl line 17.
salutations Sharon
Required by Local::MyRole

What’s your favorite?

There will always be those who insist on writing everything longhand, but modules like these can save a lot of time and typing as well as reduce errors. Do you have a favorite, maybe something I missed? Let me know in the comments.