Почему спецификация фантазийной земли требует, чтобы цепочка возвращала значение той же цепочки?

chainметод

Значение, имеющее Цепочку, должно обеспечивать chain метод. Метод цепочки принимает один аргумент:

m.chain(f)

  1. f должна быть функция, которая возвращает значение
    • Если f это не функция, поведение chain не указано
    • f должен вернуть значение той же цепочки
  2. chain должен вернуть значение той же цепочки

GitHub - сказочная страна

Дана простая реализация опции монады:

// 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(),

Другие вопросы по тегам