PDA

View Full Version : Code I wrote whilst angry



Twey
08-09-2008, 08:22 PM
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.

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);

TheJoshMan
08-09-2008, 08:23 PM
and a cow says: MOO...

Twey
08-09-2008, 08:24 PM
Only if you specialise to it. :)

mburt
08-09-2008, 08:26 PM
Uh I tested it, and all that happened was it printed a blank page.

TheJoshMan
08-09-2008, 08:27 PM
sound("MOO", new Animal("cow"));

Twey
08-09-2008, 08:27 PM
The print() statements I used are meant for running in a console, change them to alert() or something if you intend to run it in a browser.

What it does isn't remarkable, that's just standardly-idiotic sample code. What's hopefully interesting is how it does it.

mburt
08-09-2008, 08:35 PM
Yeah I'm not entirely sure what it does. It just looks like the Generic method creates functions?

Twey
08-09-2008, 08:38 PM
No, it's a crude implementation of dispatching to functions based on argument types — one way of doing classical OO in a purely-functional language. It can be thought of in terms of Haskell's typeclasses, or Common Lisp's... well, generics; that latter was the inspiration for this.

Simply, a single generic is created, and then onto that generic can be attached various specialisations to various argument types. When the generic is called, unlike a traditional function, it inspects its arguments and dispatches the call and the arguments to the appropriate specialisation. My implementation here caters to Javascript's split personality with regards to OO by allowing dispatching using both duck-typing via interfaces, and by type or instance as reported by typeof and instanceof.

mburt
08-09-2008, 08:51 PM
Ah I see... I think I understand it. A little bit, at least.

Trinithis
08-09-2008, 10:25 PM
typeof pattern === "function" || pattern instanceof Function

I tested on IE and FF and you don't need the latter half for functions.

"Structure and Interpretation of Computer Programs" (http://deptinfo.unice.fr/~roy/sicp.pdf) (large PDF link) talks about this type of dispatch if anyone is curious. (Begins talking about it on 2.4)

Twey
08-09-2008, 10:32 PM
You're right, typeof new Function() === "function". *trim*

I didn't know you still lurked around here, Trinithis :)

Trinithis
08-09-2008, 10:34 PM
I sometimes do :D

Dal
08-10-2008, 12:13 AM
Normal people punch things when they get angry!

What on earth does it actually do?

jscheuer1
08-10-2008, 02:02 AM
Interesting code. What ticked you off?

Twey
08-10-2008, 12:04 PM
Dal: I explained it in an earlier post (the interesting bit, anyway: it also prints out various animal noises, but... no-one cares about that). Trinithis also linked to a book (an excellent book, by the way, which I highly recommend) which covers this amongst other topics.
Interesting code. What ticked you off?Eh, various things.

jscheuer1
08-10-2008, 01:59 PM
Interesting code. What ticked you off?
Eh, various things.

Was there a woman involved? I guess I deserved that short answer in trade for mine. Well you certainly haven't given us any reason here not to tick you off. ;)

I especially liked (and I may be seeing the code wrong, so I guess I should say, "what looks to me like") the use of an anonymous function as an object in the global scope. But I will be scratching my head for awhile over exactly why. I thought that there wasn't much point in having an anonymous function other than getting all but its execution out of the global scope.

Twey
08-10-2008, 03:31 PM
You are indeed seeing that incorrectly, I believe. The function is called. However, there are some situations in which the pattern you describe is useful; I use it in my 'DOM' toolkit, something like this:
var Dom = (function() {
var r = function(s) {
// CSS selector stuff
};

r.byName = function(s) {
return document.getElementsByName(s);
};

return r;
})();Which allows code like:
var someEl = Dom("#some-id"),
someOtherEls = Dom.byName("and-a-name"),
andSomeMoreEls = Dom(".some-class");... where the most useful function is more easily accessible (at the cost of being named less descriptively).

Dal
08-10-2008, 03:39 PM
Fantastic stuff, Im sure I may even use this one day but I have no need for it at the moment. Im a noob at javascript, only 4 months experience. Thanks to C/C++ Im doing fine but functions like even the one above is a little too OOP for getting things done.

:)

Twey
08-10-2008, 03:52 PM
Well, you'll never have a need for it. It's a convenience. Rather than:
function foo(arg) {
if (typeof arg === "string") {
bar();
} else if (typeof arg === "number") {
baz();
} else {
quux();
}
}You would:
var foo = Generic.create(quux);
foo.specialise(['string'], bar);
foo.specialise(['number'], baz);It's particularly interesting because it's a convenience that can be used to implement a form of object orientation that's much more powerful than Java's or C++'s.

Dal
08-10-2008, 04:03 PM
I suppose theres only one question remaining in that case;

What can you come up with when your happy? ;)

Thanks for the reply Twey :)

Twey
08-10-2008, 05:29 PM
Frustratingly little. :(

Dal
08-10-2008, 05:30 PM
Frustratingly little. :(

lol :)

mburt
08-11-2008, 03:56 AM
I'm the same as Twey here... I even start typing faster and everything when I'm angry :p

Trinithis
12-27-2009, 06:26 AM
Whoa, totally forgot about this generics (Twey's) code. I happened to write something similar a few months ago for the interested:
http://codingforums.com/showthread.php?t=172313