Sunday, January 18, 2009

Multi-method dispatch for JavaScript

Once again, reporting live from the because-you-can department:
Jeremy has created an initial implementation of multi-method dispatch in Joose. It allows you to define a set of methods in a class that have the same name but differ with respect to the signature of the argument types. Once you call such a method-name, the dispatcher will look for the most appropriate method (based on the runtime types of the parameters (Are they still parameters or are they already arguments at this stage? (I think still parameters))).
Here is a link to the raw test-suite, that is currently passing.

The implementation hasn't been hooked up to a class-builder syntax and probably needs a lot of edge case work and performance tuning. However, I think it is a great example of what can be done with JS and I'm eager to try it out in practice to check whether it gives value to every-day JavaScript programming.

3 comments:

Nickolay said...

Promising start! Will the implementation follow any spec?

Jeremy said...

So my thoughts on the spec are as follows:

Order matters. In a list of patterns the first to match wins.

Can match against TypeConstraints, instanceof, or valueof.

Jeremy said...

So before I get too deep I wanted to get some feel for how/if this should be included in Joose. So first the justification.

Multi-Method dispatch me helps to segregate my code better: Error states don't clutter code that works elsewhere. The same interface can be defined for similar algorithms that work on differing types of objects. Some Examples

Class('AMultiDispatcher', {
methods: {
sort: [
{signature: [TYPE.Desc, Array],
method: function() { /* sort descending */ }
},
{signature: [TYPE.Asc, Array],
method: function() { /* sort ascending */ }
},
{signature: [Array],
method: function() { return this.sort(TYPE.Desc, Array); }
},
{signature: [],
method : function() { /*throw some sort of error condition*/ }
}
]
}
});

This is of course a very simplistic example but it demonstrates the concept. Another example would be recursion as a loop.

Class('AMultiMethodRecurser', {
methods: {
loopOverArray: [
{signature: [TYPE.EmptyArray, TYPE.Func],
method: function() { /*we are finished*/ }
},
{signature: [TYPE.Array, TYPE.Func],
method: function(ar1, processor) {
processor(ar1.shift());
this.loopOverArray(ar1, processor);
}
}
]
}
});

Again very simplistic and highly inefficient in this case, but demonstrates what is possible.

I'm also considering adding the ability to dispatch on the value that is passed in. Something like this:

Class('dispatchOnStringValue', {
methods: [
log: [
{signature: ["debug", TYPE.Str],
method: function(reason, message) { /* log a debug message */ }
},
{signature: ["info", Type.Str],
method: function(reason, message) { /* log an info message */ }
},
{signature: [Type.Str, Type.Str],
method: function(reason, message) { /* handle everything else */ }
}
]
]
});

This is actually one of my target uses.

So what does everyone think? useful or not?