Front page | perl.moose |
Postings from January 2015
Fwd: Moo: inheritance conflicting with role composition?
Thread Previous
From:
Diab Jerius
Date:
January 9, 2015 20:31
Subject:
Fwd: Moo: inheritance conflicting with role composition?
Message ID:
CADpyd5B69v3nEpJ10qJW_zt4a5Px7-jiVs80DhevYUs0FXP2XA@mail.gmail.com
On Wed, Jan 7, 2015 at 3:42 PM, Graham Knop <haarg@haarg.org> wrote:
> On 1/7/15 2:18 PM, Diab Jerius wrote:
>> Hi!
>>
>> I've attached some example code which exhibits (to my thinking) an
>> unexpected collision between inheritance and composition. I'm using
>> Moo v. 1.006001.
>>
>> There is a base role (R0) which provides a method, track().
>>
>> The role is consumed by another role (R1) which is consumed by a class
>> (C1), each of which modifies track().
>>
>> C1->new->track exhibits the correct series of modifications.
>>
>> C2 is a class which inherits from C1 and also modifies track().
>>
>> C2->new->track also exhibits the correct behavior with regard to the
>> modifications.
>>
>> C3 is a class which inherits from C1, modifies track(), and consumes R0.
>>
>> C3->new->track seems to completely ignore its parent class.
>>
>> The code outputs the packages whose modification was performed. I'm
>> getting this
>>
>> C1: R1 C1
>> C2: R1 C1 C2
>> C3: C3
>>
>> I expected the result for C3 to be
>>
>> C3: R1 C1 C3
>>
>> The order of C3's consumption of R0 and extension of C1 doesn't change
>> the results.
>>
>> What confuses me is that track() calls up the inheritance chain, so
>> that any version of track() which ends up in C3 should know enough to
>> call C1's track, and thus see the modifications made at that level.
>>
>> Please let me know if I'm missing something here!
>>
>> Thanks,
>>
>> Diab
>>
>
> The problem here is that you are trying to call the superclass of a
> role. next::method and its brethren don't work in roles. If the role
> wants to call the superclass of whatever class it is consumed by, it
> needs to use an around modifier.
Thanks.
I should have caught the incorrect use of next::method; that was silly.
My more problematic assumption was that consuming a role which was
already implicitly consumed via inheritance was a no-op. Looking at
the relevant Moo/Role::Tiny code, it looks like
1. role application doesn't check if a role has already been consumed;
2. when checking for methods to install, the inheritance hierarchy
isn't consulted
I'm sure there are good reasons for that, but I find that surprising behavior.
Your suggestion about using an around modifier was spot on. Here's
what I ended up with:
package R0 {
use Moo::Role;
use Carp;
use Sub::Name 'subname';
my $can = sub { ( shift )->next::can };
around track => sub {
# at this point, execution is at the bottom of the stack
# of wrapped calls for the immediate composing class.
# use the calling package as the starting point for the search up
# the inheritance change. as this routine gets called from
# different points, that'll change.
# only run the original method when we've reached the very
# end of the inheritance chain. otherwise it will get run
# for each class (as we bottom out here) which is incorrect.
my $orig = shift;
my $package = caller;
my $next = ( subname "${package}::track" => $can )->( $_[0] );
return $next ? $next->( @_ ) : &$orig;
};
sub track { return __PACKAGE__ }
}
Thanks a lot.
Diab
Thread Previous