The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

NEXT.pm - Provide a pseudo-class NEXT that allows method redispatch

SYNOPSIS

        use NEXT;

        package A;
        sub A::method   { print "$_[0]: A method\n";   $_[0]->NEXT::method() }
        sub A::DESTROY  { print "$_[0]: A dtor\n";     $_[0]->NEXT::DESTROY() }

        package B;
        use base qw( A );
        sub B::AUTOLOAD { print "$_[0]: B AUTOLOAD\n"; $_[0]->NEXT::AUTOLOAD() }
        sub B::DESTROY  { print "$_[0]: B dtor\n";     $_[0]->NEXT::DESTROY() }

        package C;
        sub C::method   { print "$_[0]: C method\n";   $_[0]->NEXT::method() }
        sub C::AUTOLOAD { print "$_[0]: C AUTOLOAD\n"; $_[0]->NEXT::AUTOLOAD() }
        sub C::DESTROY  { print "$_[0]: C dtor\n";     $_[0]->NEXT::DESTROY() }

        package D;
        use base qw( B C );
        sub D::method   { print "$_[0]: D method\n";   $_[0]->NEXT::method() }
        sub D::AUTOLOAD { print "$_[0]: D AUTOLOAD\n"; $_[0]->NEXT::AUTOLOAD() }
        sub D::DESTROY  { print "$_[0]: D dtor\n";     $_[0]->NEXT::DESTROY() }

        package main;

        my $obj = bless {}, "D";

        $obj->method();         # Calls D::method, A::method, C::method
        $obj->missing_method(); # Calls D::AUTOLOAD, B::AUTOLOAD, C::AUTOLOAD

        # Clean-up calls D::DESTROY, B::DESTROY, A::DESTROY, C::DESTROY

DESCRIPTION

NEXT.pm adds a pseudoclass named NEXT to any program that uses it. If a method m calls $self-NEXT::m()>, the call to m is redispatched as if the calling method had not originally been found.

In other words, a call to $self-NEXT::m()> resumes the depth-first, left-to-right search of $self's class hierarchy that resulted in the original call to m.

Note that this is not the same thing as $self-SUPER::m()>, which begins a new dispatch that is restricted to searching the ancestors of the current class. $self-NEXT::m()> can backtrack past the current class -- to look for a suitable method in other ancestors of $self -- whereas $self-SUPER::m()> cannot.

A typical use would be in the destructors of a class hierarchy, as illustrated in the synopsis above. Each class in the hierarchy has a DESTROY method that performs some class-specific action and then redispatches the call up the hierarchy. As a result, when an object of class D is destroyed, the destructors of all its parent classes are called (in depth-first, left-to-right order).

Another typical use of redispatch would be in AUTOLOAD'ed methods. If such a method determined that it was not able to handle a particular call, it might choose to redispatch that call, in the hope that some other AUTOLOAD (above it, or to its left) might do better.

Note that it is a fatal error for any method (including AUTOLOAD) to attempt to redispatch any method except itself. For example:

        sub D::oops { print "oops!\n"; $_[0]->NEXT::other_method() }

AUTHOR

Damian Conway (damian@conway.org)

BUGS AND IRRITATIONS

Because it's a module, not an integral part of the interpreter, NEXT.pm has to guess where the surrounding call was found in the method look-up sequence. In the presence of diamond inheritance patterns it occasionally guesses wrong.

It's also too slow (despite caching).

Comment, suggestions, and patches welcome.

COPYRIGHT

 Copyright (c) 2000-2001, Damian Conway. All Rights Reserved.
 This module is free software. It may be used, redistributed
    and/or modified under the same terms as Perl itself.