Base.pm
:
package Base;
use Moose::Role;
sub f {
my ($self) = @_;
print "In role.\n";
}
1;
X.pm
:
package X;
use Moose;
with 'Base';
around 'f' => sub {
my ($next, $self) = @_;
print "Nevermind, we are in class X.\n";
};
__PACKAGE__->meta->make_immutable;
1;
Y.pm
:
package Y;
use Moose;
with 'Base';
override 'f' => sub {
my ($self) = @_;
print "Nevermind, we are in class Y.\n";
};
__PACKAGE__->meta->make_immutable;
1;
Then X does work and Y does not. It is a weird design, as override
is just a special case of around
and as a special case should work also.
Can anyone explain why this design decision and why it is so weird?
$ perl X.pm
$ perl Y.pm
Cannot add an override method if a local method is already present at /usr/lib/i386-linux-gnu/perl5/5.22/Moose/Exporter.pm line 419
Moose::override('f', 'CODE(0x9c002f8)') called at Y.pm line 9
The documentation describes override
as:
An override method is a way of explicitly saying "I am overriding this method from my superclass". You can call
super
within this method, and it will work as expected. The same thing can be accomplished with a normal method call and theSUPER::
pseudo-package; it is really your choice.
What you are doing contradicts this definition. With a role, f
is installed in your package. You are attempting to define another f
in the same package.
The fact that your role is called Base
indicates to me that you have some confusion when it comes to the difference between inheritance versus composition.
The purpose of around
is to wrap a method in the current class regardless of whether it was implemented in the same package or inherited or composed:
Method modifiers can be used to add behavior to methods without modifying the definition of those methods.
Just a straightforward reading of these two snippets makes the distinction clear to me.
When you apply the role that defines f
, that itself overrides any inherited method f
. If you then say override 'f'
, you are declaring your intention to override f
again. Well, there can only be one method f
in one class. Which one should count? The one that you get by applying the role, or the one you just defined? For all intents and purposes, the methods you get from composing a role are just like methods you defined manually in the class. There is no reason a priori
one should be more important than the other.
Think of this as airline travel. Think of the method modifiers as classes: First, business, economy etc. So, in first class, get_dinner_menu
maybe wrapped with appetizers, sweets, desserts etc.
On the other hand, override
is like changing the flight. It's as if you are saying "I want to fly on both TK 1 and UIA 213". Makes no sense.
Maybe the following script will make things a bit clearer by showing a naive implementation of around
and overriding a method without using override
.
#!/usr/bin/env perl
use strict;
use warnings;
{
package MyRole;
use Moose::Role;
sub f {
print "in role\n";
}
}
{
package X;
use Moose;
with 'MyRole';
around 'f' => sub {
my ($orig, $self) = @_;
print "In wrapper\n";
return $self->$orig( @_ );
};
{
my $f = \&f;
{
no warnings 'redefine';
*f = sub {
my ($self) = @_;
print "In wrapper wrapper\n";
return $self->$f( @_ );
}
}
}
}
{
package Y;
use Moose;
with 'MyRole';
sub f {
print "In overridden method\n";
}
}
print '=-=' x 22, "\n";
my $x = X->new;
$x->f;
print '=-=' x 22, "\n";
my $y = Y->new;
$y->f;
Output:
=-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-= In wrapper wrapper In wrapper in role =-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-= In overridden method