Теперь, когда node.js поддерживает ECMAScript Harmony generators мы можем написать одноместный код лаконично ала do
блоков в Haskell:Реализация монада в JavaScript
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
, потому что вы можете только возобновить генератор из определенной позиции один раз.
Итак, мой вопрос заключается в следующем: существует ли какой-либо другой способ реализации недетерминированных монадов, таких как монашка list
, в JavaScript?
Shameless вилка: https://github.com/elclanrs/doM.js – elclanrs
@ elclanrs Это обман. Он работает, но поскольку вы создаете совершенно новую функцию, нет возможности сохранить лексическую среду исходной функции на сайте вызова. –
Что ты имеешь в виду? Не могли бы вы уточнить? – elclanrs