In his series of object orientation RFC’s Perl/Raku luminary Damian Conway includes a proposal for method redispatch, RFC 190, which is the subject of today’s article.
On method dispatch
Perl has a pseudoclass named SUPER
which an object can use to invoke a method of its parent class that it has overridden. It looks approximately like this:
sub dump_info {
my $self = shift; # obtain invocant
$self->SUPER::dump_info; # first dump parent
say $self->{derived_info}; # then ourselves
}
In this example, taken loosely from the RFC, we define a method dump_info
in a derived class which dumps the info of its parent class and then anything that itself added to the object, exemplified by the derived_info
attribute. Conway notes that this breaks down under multiple inheritance because SUPER
will only dispatch to the first parent class of $self
and once you go SUPER
you can’t go back. Supposing that all dump_info
methods in parent classes are similarly implemented, only the family bonds indicated in the below diagram with double lines would be traversed, resulting in lots of info potentially undumped:
Grand11 Grand12 Grand21 Grand22
║ │ │ │
╟────────┘ ├────────┘
║ │
Parent1 Parent2
║ │
╟─────────────────────┘
║
Derived
One might think that to get this right, each class needs to dispatch to all of its parent classes somehow, which would be akin to a post-order traversal of the inheritance tree. This is correct insofar as it models the relevance of methods in the inheritance tree, supposing that left parents are more important than right ones.
The NEXT pseudoclass
Conway’s proposal is subtly different. Namely, he proposes to add a new pseudoclass named NEXT
which is to be used just like SUPER
above and which should, instead of continuing in the parent of the current package, resume the original method dispatch process with the next appropriate candidate as if the current one had not existed. Then, method redispatch is performed with respect to the original object’s class and sees the entire inheritance tree instead of cutting off all other branches below SUPER
. Effectively, this offloads the responsibility of redispatching from each specific class onto the runtime method dispatch mechanism.
Grand11══Grand12══╗ Grand21══Grand22
║ │ ║ ║ │
╟────────┘ ║ ╟────────┘
║ ║ ║
Parent1 ╚═══Parent2
║ │
╟─────────────────────┘
║
Derived
Concretely, when calling dump_info
on an object blessed into the Derived
class, there is an array of possible candidates. They are the methods of the same name of Derived
, Parent1
, Grand11
, Grand12
, Parent2
, Grand21
and Grand22
, in order of relevance. Given this array, each dump_info
implementation just has to redispatch to the single next method in line. It is on the runtime to keep enough data around to continue the dispatch chain.
Notably, this mechanism can also provide an implementation of RFC 8, which is about the special method AUTOLOAD
. AUTOLOAD
is called as a fallback when some method name could not be resolved. Redispatching via NEXT
can be used to decline to autoload a method in the current class and leave that task to another AUTOLOAD
in the inheritance tree.
The Raku implementation
While the status of RFC 190 is “frozen”, hence accepted, this feature looks
different in Raku today. There is no NEXT
and even SUPER
is gone.
Instead we have three types of redispatch keywords:
callsame
,callwith
: calls the next candidate for the method, either
using the same arguments or with the other, given ones.nextsame
,nextwith
: the same as as thecall*
keywords, except
they do not return control to the current method.samewith
: calls the same candidate again with different arguments.
callsame
and callwith
implement the process that NEXT
would have, but in the wider context of all dispatch-related goodies that Raku got. They work in all places that have a linearized hierarchy of “callable candidates”. This includes redispatch of methods along the inheritance tree, it naturally includes candidates for multi methods and subs, it includes FALLBACK
(erstwhile AUTOLOAD
) and wrapped routines. Another speciality is in case you ever find you have to redispatch from the current method but do so in another context, for example inside a Promise, then the nextcallee
keyword can be used to obtain a Callable which can continue the dispatch process from anywhere.
We change NEXT::
→ callsame
and after some localizations, our dump_info
method looks like this and does not omit any parent class’s methods anymore:
method dump-info {
callsame; # first dump next
put $!derived-info; # then ourselves
}
In summary, being able to redispatch to a parent class’s method is useful. In a situation with multiple inheritance and multi subs, the fixation on the “parent class” is less helpful and is replaced by “less specific method”. Conway’s proposal to redispatch to the next less specific method made it into Raku and its usefulness is amplified way beyond the RFC by other Raku features and the careful design connecting them.
Curiously, a NEXT module was first shipped as a core module with Perl v5.7.3, released in 2002, written by… Damian Conway.
There is more than one way to dump info
For the particular pattern used as an example in the RFC, where each class in the inheritance tree independently throws in its own bit, there is another way in Raku to accomplish the same as redispatch. This is the method call operator .*
(or its greedy variant .+
). Consider
# Parent and grandparent classes look the same...
class Derived is Parent1 is Parent2 {
sub dump-info { put "Derived" }
}
Each dump-info
method is only concerned with its own info and does not redispatch. The .*
methodop walks the candidate chain and calls all of them, returning a list of return values (although in this case we care only about the side effect of printing to screen):
Derived.new.*dump-info
# Derived
# Parent1
# Grand11
# Grand12
# Parent2
# Grand21
# Grand22
The methods are called from most to least relevant, in pre-order of the inheritance tree. This way we can keep our dump-info
methods oblivious to redispatch, whereas explicitly redispatching using callsame
and co. would allow us to choose between pre- and post-order.
2 thoughts on “RFC 190, by Damian Conway: NEXT pseudoclass for method redispatch”