I've been thinking a lot about generic methods, aka multimethods, aka multiple dispatch. The idea, as I understand it, is that instead of dispatching a method on the type of it's class, to dispatch it on the arguments passed at runtime. I could use this while I'm rewriting Waterbear, to help simplify both the runtime code for blocks and to avoid having many blocks that only vary on the types of arguments they accept. For instance, I'd like to have an
sum block that takes any combination of numbers, arrays, and vectors (and maybe more). Two numbers are added normally. Two arrays would be concatentated. Two vectors would have their components added to produce a new vector. Things get interesting when you mix them up: a number added to a vector would be added to both components (assuming a 2D vector). A number or a vector added to an array would recursively call the
add multimethod to add the number or vector to each element of the array.
typeof operator is hopelessly broken for this use. So when my kid pointed out the nifty
type one-liner on You might not need JQuery, which almost did what I wanted. It handles all the built-in types remarkably well, but I added a couple of lines so it could also handle user-defined types.
With this in hand, I sat down tonight to see if I could write a short and sweet implementation of multimethods. I don't need (right now) full-on predicate dispatch, which can choose functions based on any arbitrary test (like "is this a number greater than 3 but less than 9?"), just being able to dispatch on the types of the arguments would be sufficient for what I need now.
And my simple implementation worked. It's not terribly efficient, but for now it seems to be working well enough for my purposes, and I can add trampolining later if the speed becomes a burden. Here is the implentation and its own test code:
Aside from the
type function, the implementation is only 22 fairly straightforward lines of code. I'm sure there are some problems with it, but based on this test I'm going to go ahead and start using it to see what I run up against. You can see in the jsbin above how it is used, but let's step through it to be clear.
First you create a new multimethod with
new Method(). Most of the methods on it are fluent (or chained). To add each function you pass two arguments the
when method (name based on Kris Jordan's implemention). Also based on his implementation I added a
default method to call when none of the other methods match. The test above has a problem if you call it with arguments that don't match anything, because the default is to just reverse two arguments. This works fine in this simple example, but would cause an infinite loop for arguments that don't match in either configuration. Once you have everything set up, you can call the
send method with your arguments, but when we're defining a multimethod like
add we want to be able to call
add.send(2,2), so we end with the
fn() call which binds
send() with the full Method object as its
this and returns us a nice encapsulated function.
I hope that's fairly clear. I was pretty jazzed about it and it solves a problem I was having. If you have a use for it, feel free to use it, and let me know if you find problems or make improvements.