Last active
January 4, 2016 20:49
-
-
Save roobie/8677191 to your computer and use it in GitHub Desktop.
Multiple dispatch
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 multi = (function multimethod_closure() { | |
/* | |
Usage: | |
var m = multi() | |
.when_t(String)(function (s) {console.log('string');}) | |
.when_t(Number)(function (s) {console.log('number');}) | |
*/ | |
"use strict"; | |
var WHEN_TYPE = "type", | |
WHEN_VALUE = "value", | |
NOOP = function () { }, | |
IDENTITY = function (a) { return a; }, | |
MULTIDENTITY = function () { return slice(arguments); }; | |
var slice = Function.prototype.call.bind(Array.prototype.slice); | |
var fluent = function fluent_decorator(fn) { | |
return function () { | |
fn.apply(this, arguments); | |
return this; | |
}; | |
}; | |
var get_ctor = function get_constructor(a) { | |
if(a === void 0 || a === null) { | |
return void 0; | |
} | |
return a.constructor; | |
}; | |
var multi = function multi(conf) { | |
conf = conf || {}; | |
var lookup = function lookup(type, args) { | |
// TODO: if dispatching on type, maybe build an algo that checks for most specific. | |
// I.e. if we have a constructor `Animal` that another constructor (e.g. `Bear`) whose | |
// prototype's [[__proto__]] points to, and user have dispatch on both types, | |
// an argument of type `Bear` should always cause the most specific fn to be invoked. | |
// It is possible with this impl, only the user have to define the least specific | |
// type to dispatch on last, that is: | |
// multi().ontypes(Bear)(func...).ontypes(Animal)(func...) | |
// otherwise the dispatch on `Animal` would catch all calls. | |
var fn_table = multimethod.function_table, | |
args_len = args.length, | |
match = false, | |
i, j; | |
var comparison; | |
if (type === WHEN_TYPE) { | |
comparison = function instance_of_type(instance, type) { | |
if (!type.prototype) { return false; } | |
return get_ctor(instance) === type || type.prototype.isPrototypeOf(instance); | |
}; | |
} else if (type === WHEN_VALUE) { | |
comparison = function strict_equals(a, b) { | |
return a === b; | |
}; | |
} else { | |
comparison = function false_func() { return false; }; | |
} | |
for (i = 0; i < fn_table.length; i++) { | |
if (fn_table[i].args.length !== args_len || fn_table[i].type !== type) { | |
continue; | |
} | |
for (j = 0; j < args_len; j++) { | |
if (!comparison(args[j], fn_table[i].args[j])) { | |
match = false; | |
break; | |
} | |
match = true; | |
} | |
if (match) { return fn_table[i]; } | |
} | |
return void 0; | |
}; | |
var multimethod = function multimethod() { | |
var args = slice(arguments, 0), | |
found_it; | |
found_it = lookup(WHEN_VALUE, args) || lookup(WHEN_TYPE, args); | |
if (!found_it && !multimethod._fallback) { | |
throw new TypeError("No match!"); | |
} | |
if (found_it) { | |
return found_it.func.apply(this, args); | |
} else if (multimethod._fallback) { | |
return multimethod._fallback.apply(this, args); | |
} | |
throw new Error("Should not happen"); | |
}; | |
var get_when_func = function get_when_func(type) { | |
return function multi_on() { | |
var self = multimethod, | |
args = slice(arguments, 0); | |
return function multi_on_callback(func, conf) { | |
conf = conf || {}; | |
self.function_table.push({ | |
type: type || WHEN_TYPE, | |
args: args || [], | |
func: func || NOOP, | |
conf: { | |
before: conf.before || MULTIDENTITY, | |
after: conf.after || IDENTITY | |
} | |
}); | |
return self; | |
}; | |
}; | |
}; | |
Object.defineProperties(multimethod, { | |
_configuration: { | |
value: Object.create(null, { | |
before: { | |
value: conf.before || MULTIDENTITY | |
}, | |
after: { | |
value: conf.after || IDENTITY | |
} | |
}) | |
}, | |
_fallback: { | |
value: null, | |
writable: true | |
}, | |
fallback: { | |
value: function set_fallback(value) { | |
this._fallback = value; | |
return this; | |
} | |
}, | |
function_table: { | |
value: [] | |
}, | |
when_t: { | |
value: get_when_func(WHEN_TYPE) | |
}, | |
when_v: { | |
value: get_when_func(WHEN_VALUE) | |
} | |
}); | |
return multimethod; | |
}; | |
return multi; | |
})(); | |
var repo = { | |
data: [124,1231,2413423,5234,234,1234,23,42,34,1234,123,452,134,123,42,134,23,4123,512,35,13,24,134], | |
filter: multi().when_t(String) | |
(multi().when_v("even") | |
(function () { | |
return this.data.filter(function (n) { | |
return n % 2 === 0; | |
}); | |
}) | |
.when_v("odd") | |
(function () { | |
return this.data.filter(function (n) { | |
return n % 2 !== 0; | |
}); | |
})) | |
.when_v("sort") | |
(function () { | |
return this.data.sort(function (a, b) { | |
return a - b; | |
}) | |
}) | |
.when_t(String, Number) | |
(function (s, n) { | |
return multi().when_v("under") | |
(function () { | |
return this.data.filter(function (q) { | |
return q < n; | |
}) | |
}) | |
.when_v("over") | |
(function () { | |
return this.data.filter(function (q) { | |
return q > n; | |
}) | |
}).call(this, s) | |
}) | |
}; | |
//repo.filter("even"); | |
//repo.filter("odd"); | |
repo.filter(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment