Skip to content

Instantly share code, notes, and snippets.

@carstenlenz
Created June 26, 2013 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carstenlenz/5866686 to your computer and use it in GitHub Desktop.
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.
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
};
})(_);
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");
});
<!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