Created
September 17, 2011 17:10
-
-
Save robotlolita/1224138 to your computer and use it in GitHub Desktop.
Multiple dispatchers in JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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