现在node.js支持ECMAScript Harmony 生成器do
,我们可以在 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);
}
在上面的代码中,monad
有一个函数可以用来创建确定性的monad,例如:
var maybe = monad(function (a) {
return {just: a};
}, function (m, f) {
return m === null ? null : f(m.just);
});
您现在可以maybe
按如下方式使用:
var readZip = maybe(function * (a, b) {
var a = yield readList(a);
var b = yield readList(b);
return _.zip(a, b);
});
上面的函数readZip
接受两个字符串,将它们转换为列表,然后压缩它们。如果有错误,则立即返回null
。它取决于以下功能:
function readList(string) {
try {
var value = JSON.parse(string);
return value instanceof Array ? {just: value} : null;
} catch (error) {
return null;
}
}
我们对其进行测试以检查它是否按预期工作:
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
同样,我们可以创建任何其他确定性 monad。例如,我最喜欢的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);
});
};
});
现在我们可以cont
简洁地使用连续传递样式创建函数:
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;
}
});
您可以fib
按如下方式使用该功能:
fib(10)(function (a) { console.log(a); }); // 55
不幸的是monad
,仅适用于确定性单子。它不适用于像list
monad 这样的非确定性 monad,因为您只能从特定位置恢复生成器一次。
list
所以我的问题是:有没有其他方法可以在 JavaScript 中简洁地实现非确定性monad?