Now that node.js supports ECMAScript Harmony generators we can write monadic code succinctly ala do
blocks in Haskell:
function monad(unit, bind) {
return function (f) {
return function () {
var g = f.apply(this, arguments);
return typeOf(g) === "Generator" ? send() : unit(g);
function send(value) {
var result = g.next(value);
if (result.done) return unit(result.value);
else return bind(result.value, send);
}
};
};
}
function typeOf(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
In the code above monad
is a function which can be used to create deterministic monads like:
var maybe = monad(function (a) {
return {just: a};
}, function (m, f) {
return m === null ? null : f(m.just);
});
You may now use maybe
as follows:
var readZip = maybe(function * (a, b) {
var a = yield readList(a);
var b = yield readList(b);
return _.zip(a, b);
});
The above function readZip
takes two strings, converts them into lists and then zips them. If there's an error then it immediately returns null
. It depends upon the following function:
function readList(string) {
try {
var value = JSON.parse(string);
return value instanceof Array ? {just: value} : null;
} catch (error) {
return null;
}
}
We test it to check whether it works as it's expected to:
console.log(readZip('[1,2,3,4]', '["a","b"]')); // [[1,"a"],[2,"b"],[3,"c"]]
console.log(readZip('hello', '["a","b"]')); // null
console.log(readZip('[1,2,3,4]', 'world')); // null
Similarly we can create any other deterministic monad. For example, my favorite, the cont
monad:
var cont = monad(function (a) {
return function (k) {
return k(a);
};
}, function (m, k) {
return function (c) {
return m(function (a) {
return k(a)(c);
});
};
});
Now we can use cont
to create functions in continuation passing style succinctly:
var fib = cont(function * (n) {
switch (n) {
case 0: return 0;
case 1: return 1;
default:
var x = yield fib(n - 1);
var y = yield fib(n - 2);
return x + y;
}
});
You can use the fib
function as follows:
fib(10)(function (a) { console.log(a); }); // 55
Unfortunately monad
only works for deterministic monads. It doesn't works for non-deterministic monads like the list
monad because you can only resume a generator from a specific position once.
So my question is this: is there any other way to implement non-deterministic monads like the list
monad succinctly in JavaScript?