25
Taming the Moose: Picking the best way to subclass Perl methods
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 use
ing 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.
25