Ramda 的 chain()
是很神奇的 Function,除了視為 flatMap()
外,也可用於 Monad,只要在 Monad 內將值也改為 Monad 時,亦可使用 chain()
。
Version
Ramda 0.27.1
chain()
chain()
Chain m => (a → m b) → m a → m b
chain()
所支援的 container 並不是 Functor,而是很獨特的 Chain,簡單說 Chain 就是 chainable,也就是支援 chain()
的 container
map()
Functor f => (a → b) → f a → f b
對比 map()
是不是非常類似呢 ?
差異僅在第一個 argument,chain()
是 a -> m b
,而 map()
是 a -> b
。
bind()
在 FP 裡,其實 signature 為 (a → m b) → m a → m b
稱為 bind()
,但因為 ECMAScript 已經有 bind()
,且 Ramda 也提供 bind()
,因此原本 FP 的 bind()
在 Ramda 只好改用 chain()
。
Monad
根據定義,Monad 必須支援 return()
與 bind()
。
- **return()**:
a -> m b
- **bind()**:
(a -> m b) -> m a -> m b
可發現 bind()
與 map()
差異是 bind()
直接傳入 return()
,而 map()
只傳入 a -> b
projection function。
但無論如何,傳入的 f()
都將直接改變 m a
內部資料。
若以 function 角度,傳入 a -> b
使的 m a
變成 m b
,這很容易理解。
但若傳入 a -> m b
,理論上應該成為 m m b
,但為什麼結果還是 m b
呢 ?
可推論 m a
內部除了 map()
外,還組合了 flatten()
,才可能使 m m b
變成 m b
。
這也是為什麼當 chain()
用在 Array 時相當於 ECMAScript 的 flatMap()
,或者相當於 pipe(map, flatten)
。
從另外一個角度,當 Maybe 內回傳 Maybe 或 Either 內回傳 Either 時,也是使用 chain()
,因為 chain()
會將兩層 Maybe 或兩層 Either 攤平。
Conclusion
chain()
用於 Array 時俗稱flatMap()
,本文以map()
與bind()
的角度,也可推論出chain()
內部就是flatMap()
chain()
若用於 Monad 則視為bind()
,只要支援chain()
的 container 就是 Chain- 當 Maybe 內回傳 Maybe,或 Either 內回傳 Either 也會使用
chain()