While working on a recent side-project1, I came across a weird bug that took me a while to untangle, and I thought I’d make a note about it before I forget.

The details of the issue are messy, and as usual, not needed to understand the core of the problem. In fact, a snippet like the one below is enough to reproduce it:

package Parent {
    use Moo;
    sub BUILD { say 'Build Parent' }
}

package Child {
    use Moo;
    extends 'Parent';
    after BUILD => sub { say 'After BUILD'  }
}
A base class and a sub-class

To provide some minimal background, Moo objects will call a method called BUILD right after they are constructed, and will do so for each of their parent classes, starting from the top-most parent.

Moo also provides a way to extend methods by means of the before, after, and around keywords, which will execute some code before, after, or instead of a given method.

In this case, the intended behaviour was that creating a Child object would run the parent BUILD, and then execute some additional code (the one specified with after).

However, creating objects of these classes resulted in the following (the comments show what would be printed):

Parent->new;
# Build Parent

Child->new;
# Build Parent
# Build Parent
# After BUILD

The parent BUILD stage runs twice (this also happens with around and before)

Wait… what?

The parent BUILD triggered twice.

As it turned out, the problem was that the child class was extending a method that it wasn’t defining, and the correct way to obtain the behaviour I expected is something like this:

package Child {
    use Moo;
    extends 'Parent';
    sub BUILD { say 'Build Child' };
}

Child->new;
# Build Parent
# Build Child
No need for modifiers, and the code does what is expected

After thinking about this for a bit2, I realised that my initial code could not possibly have been expected to work as intended: the Moo documentation clearly states that BUILD methods will be called from the top-most parent class down to the last sub-class, in that order.

Since I was installing a modifier in a sub-class, Moo could not be expected to know about it when executing the BUILD steps of the parent classes (and child classes shouldn’t really be able to mess with their parents’ setup).

But I think this sort of thing could at least come with a warning.

In fact, if you extend a method that does not exist (including a BUILD method if it doesn’t exist in the inheritance tree), you get an exception:

The method ‘BUILD’ is not found in the inheritance hierarchy for class Foo

And if you extend any other method, or at least any other that is also not one of the special ones that Moo calls on its own, the original method does not get called multiple times:

package Parent {
    use Moo;
    sub foo { say 'Foo!' };
}

package Child {
    use Moo;
    extends 'Parent';
    after foo => sub { say 'Bar!' };
}

Child->new->foo;
# Foo!
# Bar!
Extending inherited methods does not normally run them twice

In the end, this (like many other of the more interesting bugs) is more the result of a series of unfortunate events, than anything else. Complex systems are complex, and their interactions are more so.

Still, I think a warning in this case might be warranted: I’d be pressed to find a legitimate case in which someone extending a BUILD method without declaring one is not a mistake.

And I know it would have saved me a good couple of hours.

  1. Gotta love those side projects… 

  2. And getting rid of the self-righteous feeling of being upset when the computer didn’t do exactly-what-wanted-how-I-wanted…