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?
Yes, you can implement non-deterministic monads like the list monad succinctly in JavaScript using generators, à la immutagen. However, because generators in JavaScript can't be resumed from a specific position multiple times, you have to emulate this behavior by creating and replaying multiple generators. This solution has several disadvantages.
What we need in order to create non-deterministic monads such as the list monad are immutable generators. An immutable generator can be resumed from a specific position multiple times. Unfortunately, JavaScript doesn't natively support immutable generators. However, we can emulate it by creating and replaying multiple mutable generators. So, let's look at how to create an immutable generator.
The first problem we need to solve is a way is replay a mutable generator to a specific point. We do this using a special class of functions called regenerators. A regenerator is any function which returns a mutable generator. The simplest example of such a function is
function* () {}
. Thus, every generator function is a regenerator, but not every regenerator is a generator function. You can create new regenerators by advancing an old regenerator using the following function.The
next
function can be used to advance a regenerator to a specific point. For example, consider the following code snippet.As you can see, we can either advance a regenerator using the
next
function or apply a regenerator to a value to obtain a mutable generator. Now that we have the ability to replay a mutable generator to a specific point, we can create immutable generators as follows.The
immutagen
function can be used to create immutable generator functions, which we can call to yield immutable generators. Following is an example on how to create and use immutable generators.Finally, now that we have immutable generators we can implement non-deterministic monads like the list monad more succinctly as follows:
Note that the
monad
function only needsbind
. It doesn't needunit
.