Multiple dispatch is a useful generalization of traditional single dispatch that is found in any OOP language. However, it's not nearly so clear how it can be implemented in an efficient and simple way. Single dispatch is easy: any old website or textbook can tell you how objects have a vtable with a list of methods that you index into at runtime, etc. On the other hand, there are a number of resources that explain how to use multiple dispatch and why it's useful, but none give a satisfactory explanation on how it works. My question is, then, how do we do it?
Constraints
I think a satisfactory solution to the problem has a few minimally necessary traits:
- It can handle external types. So, I could have an
Animal
class in a library, and in separate code, I could create aCat
subclass that could be used in any functions in that library without the library needing modification or recompilation. - It can handle external method overrides. If there is a
attack(Animal predator, Animal prey)
function in the library, then I can make aattack(Cat predator, Mouse prey)
in my own code that will be called if anyone, me or the library or anyone else, callsattack(new Cat(), new Mouse())
. - It can handle subtypes properly in dispatch. If I make a
Lion
class but don't overrideattack(Cat predator, Mouse prey)
, thenattack(new Lion(), new Mouse())
will callattack(Cat predator, Mouse prey)
.
And ideally, an efficient implementation would have the following, although these may not always be possible:
- Multiple dispatch is not O(n) on the number of types or the number of method overrides, which could cause dispatch to be prohibitively slow as they increase.
- If a method is dispatched only on a single parameter, method dispatch is O(1) like vtable-based single dispatch.
If a multiple dispatch implementation has these traits, then I would say that it has at least the same amount of power as vtable-based single dispatch and could be called a satisfactory implementation.
Invalid solutions
There's a few solutions that I have seen thrown around that don't satisfy the above:
- The visitor pattern. This fails the first necessary trait, namely that it can handle external types. I have to have a separate method for every type I want to dispatch on. If I want more types, I need more methods, forcing recompilation of the library.
- A N-dimensional array with types on each dimension. So, if
Animal
= 0,Cat
= 1,Lion
= 2, andMouse
= 3, thenattack(new Lion(), new Mouse())
will look up atattack_vtable[2][3]
, which contains a pointer toattack(Cat predator, Mouse prey)
. Again, this presupposed a fixed number of types by merit of being able to assign a unique, consecutive number to each type and have a fixed-size array. - Do a linear search over the available methods and find the best match. This is not efficient, and is difficult to reconcile with the second trait, since the library doesn't know about the
attack(Cat predator, Mouse prey)
in our code.
I've also found some papers about multiple dispatch that I attempted to understand, but they are, at best, very dense and hard to understand, and at worst, incomprehensible to anyone without a basis in theoretical computer science.
Is there any way to implement multiple dispatch that can satisfy these constraints, or at least most of them?