前言
基於先前 Functor 與 Applicative Functor 的概念出發:
- Functor 透過
.map
轉換容器內的值,結果還是在容器內。
[2].map(x => x + 3) // [5]
- 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 的一種
開發上因為 flatMap
比 ap
更強大(它能做所有 Applicative 能做的事並有更多功能),所以不一定要實踐 ap
。