Created
June 26, 2013 11:21
-
-
Save carstenlenz/5866686 to your computer and use it in GitHub Desktop.
Clojure-inspired multimethod mechanism in JavaScript
Needs * Underscore.js (http://underscorejs.org/)
* QUnit (http://qunitjs.com/) for tests Have a look at unittests for samples.
This file contains hidden or 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 multimethod = (function(_) { | |
var method = function(multi, dispatchValue, method) { | |
multi.methodTable.push([dispatchValue, method]); | |
return method; | |
}; | |
var defaultmethod = function(multi, method) { | |
multi.default = ["default", method]; | |
return method; | |
}; | |
var multimethod = function(dispatchingFun) { | |
var multi = function() { | |
var dispatchValue = dispatchingFun.apply(this, arguments); | |
var isEqualToDispatchValue = _.compose(_.partial(_.isEqual, dispatchValue), _.first); | |
var method = _.find(multi.methodTable, isEqualToDispatchValue) || multi.default; | |
if (method) { | |
return method[1].apply(this, arguments); | |
} | |
throw "no method found to call"; | |
}; | |
multi.methodTable = []; | |
multi.method = _.partial(method, multi); | |
multi.defaultmethod = _.partial(defaultmethod, multi); | |
return multi; | |
}; | |
return { | |
multimethod: multimethod, | |
defaultmethod: defaultmethod, | |
method: method | |
}; | |
})(_); |
This file contains hidden or 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
module("multimethod"); | |
test("create multimethod with empty dispatching function", function() { | |
var hamsdi = multimethod.multimethod(function() {}); | |
ok(hamsdi != null); | |
equal(typeof hamsdi, "function"); | |
}); | |
test("calling empty multimethod should throw", function() { | |
var hamsdi = multimethod.multimethod(function() {}); | |
throws(function() { | |
var o = hamsdi(); | |
}); | |
}); | |
test("if only default method it should always be called", function() { | |
var hamsdi = multimethod.multimethod(function() {}); | |
var m = multimethod.defaultmethod(hamsdi, function() { | |
return "bamsdi"; | |
}); | |
equal(hamsdi(), "bamsdi"); | |
}); | |
test("defined method should be called when matched", function() { | |
var hamsdi = multimethod.multimethod(function(arg) { return arg; }); | |
multimethod.method(hamsdi, "value", function() { | |
return "bamsdi"; | |
}); | |
equal(hamsdi("value"), "bamsdi"); | |
}); | |
test("method with matching value should be called", function() { | |
var hamsdi = multimethod.multimethod(function(arg) { return arg; }); | |
multimethod.method(hamsdi, "value", function() { | |
return "bamsdi"; | |
}); | |
multimethod.method(hamsdi, "value2", function() { | |
return "lol"; | |
}); | |
equal(hamsdi("value"), "bamsdi"); | |
equal(hamsdi("value2"), "lol"); | |
}); | |
test("matching should support deep comparison", function() { | |
var hamsdi = multimethod.multimethod(function(arg) { return {a: arg}; }); | |
multimethod.method(hamsdi, {a: "value"}, function() { | |
return "bamsdi"; | |
}); | |
multimethod.method(hamsdi, {a: "value2"}, function() { | |
return "lol"; | |
}); | |
equal(hamsdi("value"), "bamsdi"); | |
equal(hamsdi("value2"), "lol"); | |
}); | |
test("functions should be callable on multimethod", function() { | |
var hamsdi = multimethod.multimethod(function(arg) { return {a: arg}; }); | |
hamsdi.method({a: "value"}, function() { | |
return "bamsdi"; | |
}); | |
hamsdi.method({a: "value2"}, function() { | |
return "lol"; | |
}); | |
equal(hamsdi("value"), "bamsdi"); | |
equal(hamsdi("value2"), "lol"); | |
}); | |
test("showcase: dynamic polymorphism", function() { | |
var myObj = { | |
type: 'A', | |
doIt: multimethod.multimethod(function() {return this.type;}) | |
}; | |
myObj.doIt.defaultmethod(function() { | |
return "default"; | |
}); | |
myObj.doIt.method("A", function() { | |
return "A"; | |
}); | |
myObj.doIt.method("B", function() { | |
return "B"; | |
}); | |
equal(myObj.doIt(), "A"); | |
myObj.type = "B"; | |
equal(myObj.doIt(), "B"); | |
myObj.type = "hamsdi"; | |
equal(myObj.doIt(), "default"); | |
}); |
This file contains hidden or 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>QUnit basic example</title> | |
<link rel="stylesheet" href="qunit-1.11.0.css"> | |
</head> | |
<body> | |
<div id="qunit"> | |
</div> | |
<div id="qunit-fixture"> | |
</div> | |
<script src="underscore-min.js" type="text/javascript"></script> | |
<script src="multimethod.js" type="text/javascript"></script> | |
<script src="qunit-1.11.0.js" type="text/javascript"></script> | |
<script src="test-multimethod.js" type="text/javascript"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment