black deer lying on plants near green trees during daytime

Last month I wrote about using Moose’s override function to, well, override a superclass’s method. Chris Prather on the #moose IRC channel suggested soon after that the around method modifier (or its little sisters before and after) might be a better choice if you’re also calling the original method inside. He noted that at a minimum override only works if you’re subclassing, around will apply to composed methods too.”

His point was that when you decide to compose roles (also know as traits) instead of or in addition to more traditional inheritance, override simply doesn’t work: only a method modifier will do. (And as Graham Knop and Karen Etheridge later remarked on IRC, override isn’t even an option if you’re using Moo as an alternative to Moose.)

Modifying a role’s method with around might look like this:

#!/usr/bin/env perl

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

package Local::Role::Hungry;
use Moose::Role;
requires 'name';

sub wants_food {
my $self = shift;
say $self->name, ' is hungry!';
return;
}

package Local::GuineaPig;
use Moose;
has name => (is => 'ro');
with 'Local::Role::Hungry';

around wants_food => sub {
my ($orig, $self, @args) = @_;
say $self->name, ' runs to the front of the cage!';
$self->$orig(@args);
say 'Wheek!';
return;
};

package Local::Dog;
use Moose;
has name => (is => 'ro');
with 'Local::Role::Hungry';

around wants_food => sub {
my ($orig, $self, @args) = @_;
say $self->name, ' runs to the kitchen!';
$self->$orig(@args);
say 'Woof!';
return;
};

before wants_food => sub {
my $self = shift;
say $self->name, ' is jumping!';
};

package main;
my $dog = Local::Dog->new(name => 'Seamus');
my @pigs = map { Local::GuineaPig->new(name => $_) }
qw<Cocoa Ginger Pepper>;

for my $animal ($dog, @pigs) {
$animal->wants_food();
}

Running the above yields:

Seamus runs to the kitchen!
Seamus is hungry!
Woof!
Cocoa runs to the front of the cage!
Cocoa is hungry!
Wheek!
Ginger runs to the front of the cage!
Ginger is hungry!
Wheek!
Pepper runs to the front of the cage!
Pepper is hungry!
Wheek!

It’s a little more involved than overriding a sub, since method modifiers are passed both the consumed role’s original method ($orig above) and the instance ($self above) as parameters. It has the same effect, though, and you can call the original method by saying $self->$orig(parameters).

If all you want to do is have something happen either before or after the original method, just use before or after:

before wants_food => sub {
my $self = shift;
say $self->name, ' is jumping!';
};

Note that there’s no return value in a before or after modifier, as those are handled by the original method.

Modifiers aren’t limited to consuming classes; they can be in roles and modify their consumers’ methods. They also have a couple of other tricks:

  • You can pass an array reference to modify multiple methods at once.
  • You can use the contents of a variable to specify the modified method name, and use that same variable in the modifier itself.
  • You can use a regular expression to select methods. (Beware if you’re using Moo that its Class::Method::Modifiers module doesn’t support this.)

Putting these together gives you constructs like these:

after qw<foo bar baz> => sub {
say 'Something got called';
};

for my $method_name (qw<foo bar baz>) {
before $method_name => sub {
say "Calling $method_name...";
};
}

before qr/^request_/ => sub {
my ($self, @args) = @_;
$self->is_valid(@args) or die 'Invalid arguments';
};

Moose comes with great introductory manuals for method modifiers and roles, so be sure to check those out. There’s a lot more to them and a blog can only cover so much.

Introduction: The current state of play

Perl has very minimal” support for object-​oriented (OO) programming out of the box by its own admission. It’s class-​based but classes are just packages used differently. Objects are just data structures blessed into a class, methods are just subroutines whose first argument is an object or class name, and attributes/​properties are often just the key-​value pair of a hash stored in the object. (This last is a feature shared with JavaScript, whose prototype-​based objects are just collections of key-​value pairs with the keys addressed as properties.) You’ve got polymorphism, inheritance, and it’s up to you to enforce encapsulation.

This can take a lot of work to use effectively. To help address that, several systems have been developed over the years to reduce boilerplate and provide modern (or postmodern”) OO features that developers from other languages expect. My favorite for a while has been Moo: it’s got the features I need 90% of the time like built-​in constructors, roles (an alternative to composition through inheritance), attributes, type validation, and method modifiers for enhanced polymorphism. And if I need to dig around in the guts of classes, attributes, and the like I can always upgrade to Moo’s big brother Moose and its meta-​object protocol with minimal effort.

Corinna, Object::Pad, and porting dbcritic

But there’s a new kid on the block. Curtis Ovid” Poe has been spearheading Corinna, an effort to bring effective OO to the Perl core and leapfrog [emphasis his] the capabilities of many OO languages today.” No CPAN modules, no chain of dependencies; just solid OO features and syntax built-​in. And while Corinna is a ways off from shipping, Paul LeoNerd” Evans (maybe I should get a cool nickname too?) has been implementing some of these ideas as new Perl keyword syntax in his Object::Pad module.

Both Ovid and LeoNerd have been asking developers to try out Object::Pad, not just as a new toy, but to get feedback on what works and what needs to be added. So I thought I’d try porting an older small Moo-​based project named dbcritic to this new reality. In the process, I learned some of the advantages and disadvantages of working with Object::Pad. Hopefully, this can inform both it and Corinna’s evolution as well as other curious developers’ evaluations. You can follow my coding efforts in this GitHub branch.

First, the marquee result: the code for App::DBCritic (the class I started with) is cleaner and shorter, with 33 lines shaved off so far. Mainly this is due to Object::Pad’s more concise attribute syntax (called slots” in its documentation) and lack of explicit support for Moo’s attribute coercion. I only used the latter for one attribute in the Moo version and I’m not sure it worked particularly well, so it wasn’t hard to jettison. But if your code supports coercions extensively, you’ll have to look into Object::Pad’s BUILD or ADJUST phase blocks for now.

Before, a Moo attribute with various options:

has schema => (
    is        => 'ro',
    coerce    => 1,
    lazy      => 1,
    default   => \&_build_schema,
    coerce    => \&_coerce_schema,
    predicate => 1,
);

After, an Object::Pad slot. No coercion and builder code is handled in a later ADJUST block:

has $schema :reader :param = undef;

Speaking of ADJUST blocks, it took a little bit of insight from the #perl IRC channel to realize that they were the appropriate place for setting slot defaults that are computed from other slots. Previously I was using a maze of dependencies mixing Moo lazy attributes and builder methods. Clarifying the main set of optional constructor arguments into a single ADJUST block helped untangle things, so this might be an indication that lazy attributes are an antipattern when trying to write clean code. It’s also worth noting that Object::Pad ADJUST blocks run on object construction, whereas Moo lazy attributes are only built when needed. This tends to matter for database access.

The ADJUST block for the $schema slot:

ADJUST {
    my @connect_info = ( $dsn, $username, $password );
    if ($class_name and eval "require $class_name") {
        $schema = $class_name->connect(@connect_info);
    }
    elsif ( not ( blessed($schema) and $schema->isa('DBIx::Class::Schema') ) ) {
        local $SIG{__WARN__} = sub {
            if ( $_[0] !~ / has no primary key at /ms ) {
                print {*STDERR} $_[0];
            }
        };
        $schema = App::DBCritic::Loader->connect(@connect_info);
    }
    croak 'No schema defined' if not $schema;
}

Object::Pad’s slots have one great advantage over Moo and Moose attributes: they directly support Perl array and hash data structures, while the latter only supports scalars and references contained in scalars. This means methods in your class can eliminate a dereferencing step, again leading to cleaner code. I used this specifically in the @violations array and %elements hash slots and was very pleased with the results.

The @violations and %elements slots and their ADJUST blocks:

has %elements;

ADJUST {
    %elements = (
        Schema       => [$schema],
        ResultSource => [ map { $schema->source($_) } $schema->sources ],
        ResultSet    => [ map { $schema->resultset($_) } $schema->sources ],
    );
}

has @violations;

ADJUST {
    @violations = map { $self->_policy_loop( $_, $elements{$_} ) }
        keys %elements;
}

method violations { wantarray ? @violations : \@violations }

Issues

I did have some development lifecycle issues with Object::Pad, but they’re mainly a result of its future-​facing syntax. I had to give up using perltidy and perlcritic in my build and test phases, respectively: perltidy doesn’t understand slot attributes like :reader and :param and will emit an error file (but code still compiles), and several of the perlcritic policies I use report problems because its PPI parser doesn’t recognize the new syntax. I could add exceptions in the perlcriticrc file and litter my code with more ## no critic annotations than it already had, but at this point, it was easier to just disable it entirely.

Another thing I had to disable for now was my Dist::Zilla::Plugin::Test::UnusedVars-generated Test::Vars test for detecting unused variables, as it reports multiple failures for the hidden @(Object::Pad/slots) variable. It does have options for ignoring certain variables, though, so I can explore using those and possibly file a pull request to ignore that variable by default.

Conclusion: The future looks bright

Overall I’m satisfied with Object::Pad and by extension some of the syntax that Corinna will introduce. I’m going to try porting the rest of dbcritic and see if I can work around the issues I listed above without giving up the kwalitee improvement tools I’m used to. I’ll post my findings if I feel it merits another blog.

What do you think? Is this the future of object-​oriented Perl? Let me know in the comments below.

circus theme party

Last week’s article got a great response on Hacker News, and this particular comment caught my eye:

I think this is the real point about Perl code readability: it gives you enough flexibility to do things however you like, and as a result many programmers are faced with a mirror that reflects their own bad practices back at them.

orev, Hacker News

This is why Damian Conway’s Perl Best Practices (2005) is one of my favorite books and perlcritic, the code analyzer is one of my favorite tools. (Though the former could do with an update and the latter includes policies that contradict Conway.) Point perlcritic at your code, maybe add some other policies that agree with your house style, and gradually ratchet up the severity level from gentle” to brutal.” All kinds of bad juju will come to light, from wastefully using grep to having too many subroutine arguments to catching private variable use from other packages. perlcritic offers a useful baseline of conduct and you can always customize its configuration to your own tastes.

The other conformance tool in a Perl developer’s belt is perltidy, and it too has a Conway-​compatible configuration as well as its default Perl Style Guide settings. I’ve found that more than anything else, perltidy helps settle arguments both between developers and between their code in helping to avoid excessive merge conflicts.

But apart from extra tools, Perl the language itself can be bent and even broken to suit just about anyone’s agenda. Those used to more bondage-​and-​discipline languages (hi, Java!) might feel revulsion at the lengths to which this has sometimes been taken, but per the quote above this is less an indictment of the language and more of its less methodical programmers.

Some of this behavior can be rehabilitated with perlcritic and perltidy, but what about other sins attributed to Perl? Here are a few perennial favorites”:

Objects and Object-​Oriented Programming

Perl has a minimalist object system based on earlier-​available language concepts like data structures (often hashes, which it has in common with JavaScript), packages, and subroutines. Since Perl 5’s release in 1994 much verbose OO code has been written using these tools.

The good news is that since 2007 we’ve had a sophisticated metaobject-​protocol-​based layer on top of them called Moose, since 2010 a lightweight but forward-​compatible system called Moo, and a couple of even tinier options as described in the Perl OO Tutorial. Waiting in the wings is Corinna, an effort to bring next-​generation object capabilities into the Perl core itself, and Object::Pad, a testbed for some of the ideas in Corinna that you can use today in current code. (Really, please try it—the author needs feedback!)

All this is to say that 99% of the time you never need trouble yourself with bless, constructors, or writing accessors for class or object attributes. Smarter people than me have done the work for you, and you might even find a concept or three that you wish other languages had.

Contexts

There are two major ones: list and scalar. Another way to think of it is plural” vs. singular” in English, which is hopefully a thing you’re familiar with as you’re reading this blog.

Some functions in Perl act differently depending on whether the expected return value is a list or a scalar, and a function will provide a list or scalar context to its arguments. Mostly these act just as you would expect or would like them to, and you can find out how a function behaves by reading its documentation. Your own functions can behave like this too, but there’s usually no need as both scalars and lists are automatically interpreted into lists.” Again, Perl’s DWIMmery at work.

Subroutine and Method Arguments

I’ve already written about this. Twice. And presented about it. Twice. The short version: Perl has signatures, but they’ve been considered experimental for a while. In the meantime, there are alternatives on CPAN. You can even have type constraints if you want.


I’ll leave you with this: Over the past month, Neil Bowers of the Perl Steering Council has been collecting quirks like these from Perl developers. The PSC is reviewing this collection for potential documentation fixes, bug fixes, further discussion, etc. I wouldn’t expect to see any fundamental changes to the language out of this effort, but it’s a good sign that potentially confusing features are being addressed. 

A moose

The override keyword in Perl’s Moose object system is a nice bit of code-​as-​documentation since it explicitly states that a given method overrides from its superclass. It also has a super keyword that can be used inside an override, calling the next most appropriate superclass method with the same arguments as the original method.”

The Moose documentation then goes on to say, The same thing can be accomplished with a normal method call and the SUPER:: pseudo-​package; it is really your choice.” So when should you use one and not the other? I decided to find out.

First I defined a simple Moose superclass with a single method:

package Local::MyClass;

use Moose;

sub my_method {
    return blessed $_[0];
}

__PACKAGE__->meta->make_immutable();

1;

And then a pair of subclasses, one using Moose’s override keyword and one with a plain sub:

package Local::MyClass::MyChildOverride;

use Moose;
extends 'Local::MyClass';

override my_method => sub {
    my $self = shift;
    return 'child ' . super;
};

__PACKAGE__->meta->make_immutable();

1;
package Local::MyClass::MyChildPlain;

use Moose;
extends 'Local::MyClass';

sub my_method {
    my $self = shift;
    return 'child ' . $self->SUPER::my_method();
}

__PACKAGE__->meta->make_immutable();

1;

So far so good, and both can be called successfully:

$ perl -Ilib -MLocal::MyClass::MyChildPlain \
  -MLocal::MyClass::MyChildOverride \
  -E '$PREFIX = "Local::MyClass::MyChild";
  for ( qw(Plain Override) ) {
    $object = "$PREFIX$_"->new();
    say $object->my_method()
  }'
child Local::MyClass::MyChildPlain
child Local::MyClass::MyChildOverride

Let’s toss in a new wrinkle, though. What if we forgot to define the method in the superclass?

package Local::MyClassNoMethod;

use Moose;

__PACKAGE__->meta->make_immutable();

1;

Both ways of calling the superclass’s method will bug out, of course, but unlike a plain override Moose will actually prevent you from useing the offending subclass during the BEGIN phase:

$ perl -Ilib -MLocal::MyClassNoMethod::MyChildOverride \
  -E ''
You cannot override 'my_method' because it has no super method at /Users/mgardner/.plenv/versions/5.34.0/lib/perl5/site_perl/5.34.0/darwin-2level/Moose/Exporter.pm line 419
	Moose::override('my_method', 'CODE(0x7fe5cb811a88)') called at lib/Local/MyClassNoMethod/MyChildOverride.pm line 9
	require Local/MyClassNoMethod/MyChildOverride.pm at -e line 0
	main::BEGIN at lib/Local/MyClassNoMethod/MyChildOverride.pm line 0
	eval {...} at lib/Local/MyClassNoMethod/MyChildOverride.pm line 0
Compilation failed in require.
BEGIN failed--compilation aborted.

With plain method overriding, you only get an error if you try to call the superclass’s method. If your overridden method doesn’t do that, it’s perfectly safe to define and call. It’s only if you use that SUPER:: pseudo-​package that things blow up at runtime:

$ perl -Ilib -MLocal::MyClassNoMethod::MyChildPlain \
  -E '$obj = Local::MyClassNoMethod::MyChildPlain->new();
  $obj->my_method()'
Can't locate object method "my_method" via package "Local::MyClassNoMethod::MyChildPlain" at lib/Local/MyClassNoMethod/MyChildPlain.pm line 8.

Note that none of this is caught at compile time. perl -c will happily compile all these classes and subclasses without a peep:

$ find . -name '*.pm' -exec perl -c {} \;
./lib/Local/MyClass/MyChildPlain.pm syntax OK
./lib/Local/MyClass/MyChildOverride.pm syntax OK
./lib/Local/MyClassNoMethod/MyChildPlain.pm syntax OK
./lib/Local/MyClassNoMethod/MyChildOverride.pm syntax OK
./lib/Local/MyClass.pm syntax OK
./lib/Local/MyClassNoMethod.pm syntax OK

So what can we conclude? Moose’s override is a good way of describing your intent with a subclass, and it will catch you out if you try to use it without a corresponding method in a superclass. It is a non-​standard keyword though, so syntax-​highlighting editors and code analysis tools won’t recognize it unless taught. Further, if your subclass method doesn’t call the same method in a superclass you could eventually get away with removing the latter if you use a plain sub.

I’ve created a small GitHub project with the sample code from this article, including test scripts.

What do you think? Is override suitable for your Moose projects, or are you satisfied with plain sub? Let me know in the comments.