Simple Explanation of Functor

實際簡白的 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