Теперь, когда node.js поддерживает генераторы гармоник ECMAScript, мы можем написать монодичный код лаконично ala 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 есть функция, которая может использоваться для создания детерминированных монад:
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
Аналогично мы можем создать любую другую детерминированную монаду. Например, моя любимая, монада cont:
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, потому что вы можете только один раз возобновить генератор из определенной позиции.
Итак, мой вопрос заключается в следующем: есть ли другой способ реализовать в JavaScript недетерминированные монады, такие как монаха list?