DEV Community

loading...
Cover image for Taming the Moose: Picking the best way to subclass Perl methods

Taming the Moose: Picking the best way to subclass Perl methods

Mark Gardner
I help professional Perl developers to engineer modern, disciplined applications in the cloud.
Originally published at phoenixtrap.com on ・3 min read

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (0)