I’m starting to learn Scala, and I love their for-comprehension style of monadic programming. Here’s a small library that uses eval and textual manipulations to do desugaring.
// A Monad class for Array and other monads to inherit from function Monad() { throw "abstract"; } Monad.prototype.flatMap = function (f) { return this.map(f).flatten(); }; // Make Array inherit from Monad Array.prototype.__proto__ = Monad.prototype; // Define the monadic multiplication Array.prototype.flatten = function () { var result = []; var len = this.length; for (var i = 0; i < len; ++i) { result = result.concat(this[i]); } return result; }; // The comprehension class var M = function () { var pairs = []; this.for = function (v, e) { pairs.push({e: e, v: v}); return this; }; this.yield = function (body) { var desugared = pairs.map(function (pair, index) { return '(' + pair.e + ').' + ((index === pairs.length-1) ? 'm' : 'flatM') + 'ap(function(' + pair.v + ') { return '; }).join('') + '(' + body + ');' + pairs.map(function () { return ' });' }).join(''); pairs = []; return desugared; }; }; // Avoid the need to say (new M) at the start M.for = function (v, e) { return (new M).for(v, e); }; // Tensor product of lists // eval(M.for('x','[1,2,3]').for('y','[5,6,7]').yield('x * y')); // 5,6,7,10,12,14,15,18,21 // Aka Maybe function Option() { throw "abstract"; } Option.prototype = Object.create(Monad.prototype); Option.prototype.map = function (f) { if (this instanceof None) { return this; } return new Some(f(this.value)); }; Option.prototype.flatten = function () { if (this instanceof None) { return this; } return this.value; }; function None() {} None.prototype = Object.create(Option.prototype); None.prototype.toString = function () { return 'None'; }; function Some(v) { this.value = v; } Some.prototype = Object.create(Option.prototype); Some.prototype.toString = function () { return 'Some(' + this.value + ')'; }; // var xs = [1,2,3]; // eval(M.for('x','xs').for('y','x % 2 ? new Some(x) : new None()').yield('x * y')); // [Some(1), None, Some(9)]
The call to eval
on the outside is necessary to capture the scope in which the for-comprehension occurs (e.g. the scope in which xs
occurs in the second example).