http://qs321.pair.com?node_id=1039934

QM has asked for the wisdom of the Perl Monks concerning the following question:

I'm poking around with inheritance, and need to call the grandparent's method, skipping the parent's method. What's the simplest way to do that?

I'd prefer something like

$x->SUPER::SUPER::method;

But SUPER is Perl magic, and doesn't work that way.

In my current case, it is OK to redefine/undefine the parent's method, though I haven't figured out how to do that without knowing the parent's name. (Perhaps I've overlooked something.) I'd like to know how to skip the parent's method temporarily too.

I'm also wondering if can might be used in a cleaner approach.

Update:

I prefer not to use modules outside the core, though I can do so if necessary. (Lots of machines to update, etc.)

In the meantime, I'm examining a solution like this, which looks a bit like one of tobyink's suggestion.

# Within the child, get the parent’s class name from @ISA my ($parent) = @ISA; print "\$parent = <$parent>\n"; # Now get the grandparent’s class name from the parent’s @ISA my $grandparent; { no strict 'refs'; ($grandparent) = @{"${parent}::ISA"}; } # Get a reference to the grandparent’s method “me” my $y = $grandparent->can('me'); $y->($object);

-QM
--
Quantum Mechanics: The dreams stuff is made of

Replies are listed 'Best First'.
Re: Call Grandparent Method, Skipping Parent
by tobyink (Canon) on Jun 20, 2013 at 11:53 UTC

    Just hardcode the package name you wish to skip to:

    $self->SomeClass::method(...);

    Example:

    #!/usr/bin/env perl use strict; use warnings; { package Animal; sub foo { my $class = shift; print __PACKAGE__, "\n"; } } { package Mammal; use base "Animal"; sub foo { my $class = shift; print __PACKAGE__, "\n"; $class->SUPER::foo(@_); } } { package Primate; use base "Mammal"; sub foo { my $class = shift; print __PACKAGE__, "\n"; $class->SUPER::foo(@_); } } { package Monkey; use base "Primate"; sub foo { my $class = shift; print __PACKAGE__, "\n"; $class->Mammal::foo(@_); # skip over Primate::foo } } Monkey->foo;

    Here's an alternative Monkey::foo sub that avoids hard-coding the Mammal class name:

    sub foo { my $class = shift; print __PACKAGE__, "\n"; require mro; my $grandparent = $class->mro::get_linear_isa->[2]; my $gp_method = $grandparent->can('foo'); $class->$gp_method(@_); }

    Or even:

    sub foo { my $class = shift; print __PACKAGE__, "\n"; require mro; $class->${\ $class->mro::get_linear_isa->[2]->can("foo") }(@_); }

    Note that mro requires Perl 5.10 or better, but there exists a MRO::Compat that provides an alternative implementation of mro::get_linear_isa for Perl 5.8.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      Thanks. See the update to the OP.

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

        mro has been in the core for about 6 years.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Call Grandparent Method, Skipping Parent
by derby (Abbot) on Jun 20, 2013 at 12:35 UTC

    At the risk of downvotes -- Yuck, Yuck and triple yuck. What beastly mis-modeling deeds would lead you down this road?

    -derby
      Yuck, Yuck and triple yuck.
      Yes, agreed.

      I have a script XXX, written as a module that can be inherited from. A second script YYY inherits from XXX, but doesn't want to call XXX->setup. YYY does need to call the grandparent WWW->setup.

      As YYY needs other methods from both, it's not clear how to manage this generically in a large code environment, where modules are run as scripts, but may also be inherited from in the future.

      If the grandparent's name changes, or the number of intervening parents to skip changes, code will need updating. Perhaps it would be better in the grandparent's new to register some value for its children to reference, to know the name of the "interesting" ancestor?

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

        Why not refactor XXX into ZZZ (which holds everything but XXX->setup) and XXX which inherits from ZZZ and only has distinctive setup? YYY can now inherit from ZZZ as well.
        WWW (+setup) WWW (+setup) +-XXX (+setup) => +-ZZZ +-YYY +-XXX (+setup) +-YYY

        "I have a script XXX, written as a module that can be inherited from."

        Don't do that. Write a module and then write a simple script that accesses the module.

        "If the grandparent's name changes, or the number of intervening parents to skip changes, code will need updating."

        Which is why you should be using Roles instead of Inheritance, generally speaking. See Inheritance Versus Roles

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: Call Grandparent Method, Skipping Parent (can smells)
by Anonymous Monk on Jun 20, 2013 at 10:42 UTC

    I think you can use SUPER , say using find_parent , or by copying find_parent

      haha, I messed up, I didn't mean to imply can smells in any way :) I was gonna say you can use can ... and then I found SUPER

      The other thing I was gonna say is : I'm no OO expert, but even I know this smells :)

      See update in OP.

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of