Perl’s default inheritance mechanism is a bit weird, and it’s not something that any language designer would want to repeat. However, it is what it is and by knowing its quirks you should have any easier time tracking down inheritance problems.
Before you go further in this Item, though, you have to sit through this safety announcement, which we are going to announce loudly with an over-amped PA system: DON’T USE MULTIPLE INHERITANCE IF YOU CAN DO IT ANY OTHER WAY. Often, multiple inheritance is a crutch for either a bad design or a missing language feature. It also kills kittens. Try a different relationship or check out roles or traits to see if those might work for you. That’s the short story.
Now, having been warned, imagine this inheritance structure where you have two branches:
The idea of inheritance is that the lower classes don’t have to redo all of the work of the higher classes. The child classes only have to implement the parts that are different from the parent classes. If the child class doesn’t need to change a method, it doesn’t need to declare it at all. Through inheritance, you’ll eventually find the place where you defined the method and use that.
In Perl, that inheritance structure translates roughly to a series of packages that each define their upward connection:
use 5.014; package A { ... } package B { our @ISA = 'A'; ... } package X { ... } package Y { our @ISA = 'X'; ... } package F { our @ISA = qw(B Y) }
You use the @ISA
special variable here. You could use the parent
pragma with -norequire
since your classes are in their own files, but that’s a lot of work to just do the simple @ISA
assignment.
Now, you call a method on an object from F. How does Perl find the class that implements the method? That is, what is Perl’s method resolution order? The method could be in F or in any of its parent classes? perl
does a left-first, depth-first search. It looks in the order (F, B, A, Y, X). And perl
uses the first implementation it finds.
UNIVERSAL
There’s more. The correct way to state this situation sounds overly simple. Perl searches through the classes you list. After Perl tries all of the subclasses you listed, in all of the branches of inheritance, it moves on to UNIVERSAL
, as if you had said this:
package F { use parent qw(B Y UNIVERSAL) }
After Perl searches through the classes (and their parent classes) that you have explicitly defined and still doesn’t find the method, it tries UNIVERISAL
. This is different from other systems where every class inherits from something like Object
such that the ultimate parent class sits at the top.
Now, that’s a bit weird, you should think, because each of the parent classes should also have this behavior, right? In effect, it only applies to the class that you started with. To say that in another way, only the original class appears to inherit from UNIVERSAL
. Curiously, this isn’t stated succinctly anywhere in the core perl
documentation. It’s only clear in Chapter 12 in Programming Perl.
AUTOLOAD
There’s even more. If perl
does not find the method in UNIVERSAL
, it starts all over in the original class looking for the special method name AUTOLOAD
, which we aren’t going to explain in this Item. The order turns out to be this:
- F::some_method
- B::some_method
- A::some_method
- Y::some_method
- X::some_method
- UNIVERSAL::some_method
- F::AUTOLOAD
- B::AUTOLOAD
- A::AUTOLOAD
- Y::AUTOLOAD
- X::AUTOLOAD
- UNIVERSAL::AUTOLOAD
Now, here’s the trick. Once perl
finds one of those methods, including AUTOLOAD
, it stops looking. There’s no way for an AUTOLOAD
to defer itself so perl
can keep searching. If the AUTOLOAD
doesn’t want to handle it, you have to jump through black magic hoops to keep the process going.
Things to remember
perl
searches through@ISA
left first and depth first.- After going through
@ISA
,perl
triesUNIVERSAL
for the original class. - Failing
UNIVERSAL
,perl
does another left first and depth first search looking forAUTOLOAD
.