“I used to believe that multimethods were so advanced I would never need them. Well, maybe I still believe that, but here’s a quick and dirty implementation of multimethods so you can see for yourself. Some assembly required; advanced functionality left as an exercise for the reader.” – Guido Van Rossum
Why might you need multimethods? If you read the BFDL’s post, he makes absolutely no case for them. Let’s take a look at what Wikipedia has to say on the subject…
What’s interesting here is the recurring example – it’s one I’ve seen before, relating to dispatch, of collisions between objects in a simple computer game. What’s so interesting is that collisions between objects in a simple 2D computer game is one of the few compelling examples of why multi-methods are required I’ve ever come across – and yet this particular example doesn’t retain any of details as to why it’s a compelling example. Let’s look at how it might be implemented:
So, you’ve got all sorts of objects moving around on a screen, some of which are best modelled using circles and some of which are best modelled using rectangles. How do you determine if two objects have collided with each other?
If they’re both circles, you simply compare the distance between their midpoints and check if it’s less than the sum of their radii. If it is, they’re colliding: otherwise they aren’t.
If they’re both rectangles, you see if the right side of one is left of the left side of the other, or the left side of one is right of the right side of the other, or the top of one is below the bottom of the other, or the bottom of one is above the top of the other. If any are true, they’re not colliding: otherwise they are.
If one’s a rectangle and the other’s a circle, it’s a bit more complicated, depending on where the center of the circle is relative to the edges of the rectangle, but it’s still quite simple.
The point here being: when we’re abstracting over a list of objects of indeterminate shape (either a rectangle or circle), the formula we apply to determine whether a collision takes place or not depends on both types. It can’t be addressed by converting one type to the other, or converting both to a common type: we need a different implementation for each combination of types. So here we have a situation where multimethods are an appropriate solution.
Not, by any means, the only solution – you could have a function for each and a wrapper function which examines the types and calls the correct method. You could use double dispatch (please, don’t ever use double dispatch). You could, if the language supported it, use a pattern-matching construct – of course, that’s not an option in Python.
All of these approaches basically amount to the same thing, though: runtime examination of the type of each argument, and then dispatching to the appropriate implementation. Multi-methods are simply a syntactic sugar which expresses that concern more succinctly than any of the above (although of course pattern-matching does a lot more than just this).
So, given that problems exist for which multimethods are a useful, how can you go about implementing them? Enter Guido’s post, which provides a quick and dirty implementation.
It’s definitely quick. It’s also, unfortunately, very dirty, and has some annoying limitations and surprising behaviours. We’ll use it as a starting point, and try to turn it into something actually usable…