Skip to content

Instantly share code, notes, and snippets.

@carymrobbins
Last active August 29, 2015 14:01
Show Gist options
  • Save carymrobbins/01ca856501071c0d4415 to your computer and use it in GitHub Desktop.
Save carymrobbins/01ca856501071c0d4415 to your computer and use it in GitHub Desktop.
Algebraic Data Types for JavaScript.
var assert = require('assert'),
typed = require('./typed.js'),
data = typed.data,
matcher = typed.matcher;
// This will define a Maybe type with two constructors.
var Maybe = data(a => [Just(a), Nothing]);
// We can perform pattern matching on the constructor name.
var fromJust = matcher({ Just: x => x });
assert.equal(fromJust(Just(1)), 1);
assert.equal(fromJust(Nothing), undefined);
var catMaybes = xs => xs.filter(matcher({ Just: _ => true })).map(matcher({ Just: x => x }))
var maybes = [Just(1), Nothing, Just(3), Nothing, Just(5)];
assert.deepEqual(catMaybes(maybes), [1, 3, 5]);
// Multiple type variables are allowed.
var Either = data((a, b) => [Left(a), Right(b)]);
var lefts = xs => xs.filter(matcher({ Left: _ => true}),)
rights = xs => xs.filter(matcher({ Right: _ => true }));
var eithers = [Left(1), Right(2), Left(3), Right(4), Left(5)];
assert.equal(lefts(eithers).length, 3);
assert.equal(rights(eithers).length, 2);
var esprima = require('esprima');
var data = function (def) {
var cons = function () { return undefined; },
ast = esprima.parse('_ = ' + def.toString()).body[0].expression.right,
adts = ast.body.body[0].argument.elements,
self = this;
adts.forEach(function (adt) {
var adtName, adtArgs;
if (adt.type === 'CallExpression') {
adtName = adt.callee.name;
adtArgs = adt.arguments;
self[adtName] = function () {
if (adtArgs.length !== arguments.length) {
throw Error(adtName + 'expects ' + adtArgs.length +
' argument(s), not ' + arguments.length);
}
var result = new cons();
result._name = adtName;
result._args = arguments;
return result;
};
} else {
adtName = adt.name;
var x = new cons();
x._name = adtName;
x._args = [];
self[adtName] = x;
}
});
return cons;
};
var matcher = function (patterns) {
return function (value) {
var k;
for (k in patterns) {
if (patterns.hasOwnProperty(k)) {
if (value._name === k) {
return patterns[k].apply(this, value._args);
}
}
}
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment