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 scripts archives, so I'll just leave it lying about here.
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
};
})();
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);
Bookmarks