Generics in Javascript
by
, 02-23-2009 at 11:16 PM (26653 Views)
I just used this code to vent some frustration earlier, hope you all like it. It's pretty self-explanatory. It's probably too theoretical to go into the DD script archives, so I'll just leave it lying about here.
Some sample code:Code:var Generic = (function() { var MATCH_FAIL = {}, MATCH_ANY = function() { return MATCH_ANY; }; function create(fallback) { var s = function() { for (var i = 0, args = Array.prototype.slice.call(arguments), r; i < specs.length; ++i) if ((r = specs[i].match(args)) !== MATCH_FAIL) return r; if (s._fallback) return s._fallback.apply(this, args); throw new Error("Generic: No methods matched and no fallback provided."); }, specs = s._specs = []; s.specialise = specialise; s.addFallback = addFallback; if (fallback) s.addFallback(fallback); return s; } function specialise(patterns, func) { var s = this._specs; s[s.length] = new Specialisation(patterns, func, this); return this; } function addFallback(func) { this._fallback = func; return this; } /**** Begin Specialisation ****/ function Specialisation(patterns, func, context) { this.patterns = patterns; this.func = func; this.context = context; } Specialisation.compatible = function(value, pattern) { if (pattern === MATCH_ANY && value !== undefined) return true; else if ((typeof pattern === "string" || pattern instanceof String) && typeof value === pattern) return true; else if (typeof pattern === "function" && value instanceof pattern) return true; else if (pattern instanceof Pattern) return pattern.guard(value); else if (pattern instanceof Interface) return pattern.match(value); return false; }; Specialisation.prototype = { match: function(args) { for (var i = 0, a = this.patterns, n = a.length; i < n; ++i) if (!Specialisation.compatible(args[i], a[i])) return MATCH_FAIL; return this.func.apply(this.context, args); } }; /**** Begin Pattern ****/ function Pattern(guard) { this.guard = guard; } function GUARD(func) { return new Pattern(func); } function GUARD_IS(right) { return new Pattern(function(val) { return val === right; }); } /**** Begin Interface ****/ function Interface(obj) { if (!(this instanceof Interface)) return new Interface(obj); for (var x in obj) if (obj.hasOwnProperty(x)) this[x] = obj[x]; } Interface.getSkeleton = function(obj) { var r = {}; for (var x in obj) if (obj.hasOwnProperty(x)) r[x] = typeof obj[x]; return new Interface(r); }; Interface.prototype = { match: function(value) { for (var x in this) if (this.hasOwnProperty(x) && !Specialisation.compatible(value[x], this[x])) return false; return true; } }; return { create: create, Interface: Interface, GUARD: GUARD, GUARD_IS: GUARD_IS, MATCH_ANY: MATCH_ANY }; })();
Code:function Animal(species) { this.species = species; } var iAnimal = Generic.Interface.getSkeleton(new Animal("foo")); var iDog = Generic.Interface({'species' : Generic.GUARD_IS("dog")}); var sound = Generic.create() .specialise([iDog, 'undefined'], function() { print("I got a dog! I got one!"); }) .specialise(['string', iAnimal], function(snd, an) { print("A " + an.species + " says '" + snd + "',"); }) .specialise([iAnimal, 'string'], function(an, snd) { print("but a " + an.species + " says '" + snd + "',"); }) .specialise([Animal], function(an) { print("and a " + an.species + " is apparently silent."); }) .specialise([Generic.MATCH_ANY, 'undefined'], function(val) { print("... and whatever noise a " + val + " makes..."); }) .addFallback(function() { print("Er... random animal noise?"); }); sound(new Animal("dog")); sound("woof", new Animal("dog")); sound(new Animal("cat"), "miaow"); sound(new Animal("frog")); sound(42); sound(4, 5);