A Simple Explanation of Monad

实际简单的 Monad 解释

前言

基于先前 FunctorApplicative Functor 的概念出发:

  1. Functor 通过 .map 转换容器内的值,结果还是在容器内。
[2].map(x => x + 3) // [5]
  1. Applicative 允许把“包起来的函数「套到」包起来的值”上,运算还是在包装中发生。
Maybe.of(x => x + 3).ap(Maybe.of(2)) // Maybe(5)

但如果包装内返回内容被包装该怎么办呢?

Maybe.of(2).map(x => Maybe.of(x + 3))
// 结果是 Maybe(Maybe(5))

什么是 Monad?

function Maybe(value) {
return value == null ? Nothing() : Just(value);
}
function Just(value) {
return {
map: (fn) => Maybe(fn(value)),
flatMap: (fn) => fn(value),
getOrElse: () => value,
};
}
function Nothing() {
return {
map: () => Nothing(),
flatMap: () => Nothing(),
getOrElse: (defaultValue) => defaultValue,
};
}

通过 flatMap(或叫 chain)会在运算后自动把包装摊平,让运算可以顺利的接续下去:

getUser(id)
.flatMap(user => getProfile(user))
.flatMap(profile => getPosts(profile))

举例数组:

const arr = [1, 2, 1];
const result = arr.flatMap((num) => (num === 2 ? [2, 2] : 1));
console.log(result);
// Expected output: Array [1, 2, 2, 1]

Monad 定义

  • Left Identity

    // 将值放入容器后,立刻 flatMap 一个函数
    // 等同于直接把该值传进该函数
    Monad.of(a).flatMap(f) ≡ f(a)
    // 示例
    const f = x => Maybe.of(x + 1)
    Maybe.of(2).flatMap(f) // => Maybe(3)
    f(2) // => Maybe(3)
  • Right Identity

    // 对一个 Monad flatMap of
    // 等同于什么都不做,这表示 flatMap 不会破坏原本的结构。
    m.flatMap(Monad.of) ≡ m
    // 示例
    Maybe.of(2).flatMap(Maybe.of) // => Maybe(2)
    Maybe.of(2) // => Maybe(2)
  • Associativity

    // 连续 flatMap 两个函数
    // 等同于 flatMap 一个合成函数
    // 这保证了多层链接时,不论括号怎么放,结果都一致。
    m.flatMap(f).flatMap(g)
    m.flatMap(x => f(x).flatMap(g))
    // 示例
    const f = x => Maybe.of(x + 1)
    const g = x => Maybe.of(x * 2)
    Maybe.of(2).flatMap(f).flatMap(g) // => Maybe(6)
    Maybe.of(2).flatMap(x => f(x).flatMap(g)) // => Maybe(6)

总结

Monad ⊇ Applicative ⊇ Functor
  • Monad 是 Applicative 也是 Functor 的一种
  • Applicative 是 Functor 的一种

开发上因为 flatMapap 更强大(它能做所有 Applicative 能做的事并有更多功能),所以不一定要实现 ap