Skip to content

Instantly share code, notes, and snippets.

@robotlolita
Created September 17, 2011 17:10
Show Gist options
  • Save robotlolita/1224138 to your computer and use it in GitHub Desktop.
Save robotlolita/1224138 to your computer and use it in GitHub Desktop.
Multiple dispatchers in JS
var slice = [].slice
function but_last(seq) {
return seq.slice(0, -1)
}
function last(seq) {
return seq[seq.length - 1]
}
function match_predicates(predicates, args) { var weight
function pass(predicate, index) {
return predicate(args[index], index, args, predicates) }
function match_all() {
return predicates.every(function(predicate, index) {
if (pass(predicate, index)) return (weight += (predicate.specificity || 1)), true
return false }) }
weight = 0
if (match_all()) return weight
}
function match_handler(fn, args) { var handler, handlers
function specificity(lhs, rhs) {
return lhs[0] - rhs[0] }
handlers = fn.signatures.map(function(signature) { var predicates, code
predicates = but_last(signature)
code = last(signature)
return [ match_predicates(predicates, args)
, code ] })
handlers = handlers.filter(function(handler){ return handler[0] })
handler = last(handlers.sort(specificity))
return handler && handler[1]
}
function make_generic(fn) {
function exec() { var args, callee
args = slice.call(arguments)
callee = match_handler(exec, args) || exec.fallback
console.log('using ' + callee.toString(), 'for arguments ', args)
return callee.apply(this, args)
}
exec.signatures = []
exec.fallback = fn
return exec }
// -----------------------------------------------------------------------------
function class_of(obj) { return {}.toString.call(obj) }
function numericp(obj) { return !isNaN(obj) }
function nump(obj) { return class_of(obj) === '[object Number]' }
function restp(obj, idx, args, preds) { return args.length >= preds.length
&& idx === preds.length - 1 }
nump.specificity = 100
numericp.specificity = 10
restp.specificity = 1
var sum = make_generic(function() { return 0 })
sum.signatures = [
[ numericp
, function(num){ return +num }]
,[ nump, numericp
, function(lhs, rhs) { return lhs + sum(rhs) }]
,[ numericp, nump
, function(lhs, rhs) { return sum(lhs) + rhs }]
,[ numericp, numericp
, function(lhs, rhs) { return sum(lhs) + sum(rhs) }]
,[ nump, nump
, function(lhs, rhs) { return lhs + rhs }]
,[ numericp, numericp, restp
, function(lhs) { return sum(lhs, sum.apply(this, slice.call( arguments, 1))) }]
]
// tests
function test(expr, result, expected) {
console.log( result === expected? '[OK] ' : '[FAIL] '
, expr + ' => ' + result + ' === ' + expected
, '\n\n') }
test('sum(undefined )', sum(undefined ), 0)
test('sum(null )', sum(null ), 0)
test('sum([3] )', sum([3] ), 3)
test('sum(-3 )', sum(-3 ), -3)
test('sum("3" )', sum("3" ), 3)
test('sum(2, 3 )', sum(2, 3 ), 5)
test('sum(2, "3" )', sum(2, "3" ), 5)
test('sum("2", "3" )', sum("2", "3" ), 5)
test('sum("2", [4], 3 )', sum("2", [4], 3 ), 9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment