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:
To provbide some minimal background, Moo objects will call a method
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
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
would run the parent
BUILD, and then execute some additional code (the one
However, creating objects of these classes resulted in the following (the comments show what would be printed):
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:
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
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:
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
without declaring one is not a mistake.
And I know it would have saved me a good couple of hours.