Почему спецификация фантазийной земли требует, чтобы цепочка возвращала значение той же цепочки?
chain
методЗначение, имеющее Цепочку, должно обеспечивать
chain
метод. Метод цепочки принимает один аргумент:
m.chain(f)
f
должна быть функция, которая возвращает значение
- Если
f
это не функция, поведениеchain
не указаноf
должен вернуть значение той же цепочкиchain
должен вернуть значение той же цепочки
Дана простая реализация опции монады:
// prototypes:
const someProto = {
of(x) { return some(x) },
map(f) { return some(f(this.x)) },
ap(ftor) { return ftor.map(this.x) },
join() { return this.x },
chain(mf) { return this.map(mf).join() }
};
const noneProto = {
of() { return this },
map() { return this },
ap() { return this },
join() { return this },
chain() { return this }
};
// factories:
function some(x) {
return Object.assign(Object.create(someProto), {x: x});
}
function none() {
return Object.assign(Object.create(noneProto), {x: null});
}
Чтобы гарантировать, что chain
всегда возвращает опцию монады, я должен убедиться, что mf
(монадическая функция) всегда возвращает единицу. Это невозможно, потому что mf
не является частью реализации. Скорее это определяется, когда используется монада:
// auxiliary function:
const sub = y => x => x - y;
let a = some(2);
let b = some(3);
a.chain(x => b.chain(y => some(sub(x)(y)))); // {x: 1}
a.chain(x => b.chain(y => sub(x)(y))); // 1 - ouch!
Во втором приложении метода переданная функция не возвращает монаду, что приводит к развернутому результату монадического вычисления. Я мог бы добавить проверку типа chain
или же join
может быть, с помощью утки, чтобы решить проблему - это было бы довольно уродливо, хотя.
Почему спецификация требует безопасности типов именно в этот момент? Javascript динамически типизирован, и я бы предпочел писать соответствующие модульные тесты вместо выполнения проверок типов во время выполнения. Буду ли я нарушать спецификации тогда?
1 ответ
Во втором примере вы должны использовать .map ():
a.chain(x => b.map(y => sub(x)(y)));
и тогда все следует правилам.
Для сравнения вот эквивалентные подписи на Haskell:
fmap :: m a -> (a -> b) -> m b -- same as .map()
(>>=) :: m a -> (a -> m b) -> m b -- same as .chain()
Так что если ваша функция возвращает монадическое значение, используйте .chain()
, В противном случае используйте .map()
,