練習 chain()

chain() 屬於 Ramda 較高級的 operator,在其他 FP 也稱為 flatMap()apply(),尤其當 data 為 function 時,將有意想不到的作用。

Version


WebStorm 2018.3.3
Quokka 1.0.134
Ramda 0.26.1

Ramda


chain()

Chain m => (a → m b) → m a → m b
對 array 或 function 先執行 map() ,再將結果結合起來

mChain 型別,其定義可參考 Fantacy Land,先簡單想成可以是 array 或 function 即可。

(a -> m b):第一個參數為 function,由任意型別轉成 array 或 function

m a:第二個參數為 array 或 function

m b:回傳為另一個 array 或 function

Array


m 為 array 時,第二個參數 data 及為 array。

1
2
3
4
5
6
7
import { chain, map, flatten } from 'ramda';

const duplicate = x => [x, x];
const data = [1, 2, 3, 4];

console.log(flatten(map(duplicate, data)));
console.log(chain(duplicate,data));

duplicate()a -> m b 的 function,符合 chain() 第一個參數的要求。

map() 搭配 duplicate() 時,無疑會產生 nested array,為了將 array 攤平,我們可再搭配 Ramda 的 flatten() operator 將所有 array 給 concatenate 起來成一層 array。

事實上 flatten()map() 可使用 chain() 取代,這也是為什麼 chain() 也稱為 flatMap()

chain000

Function


m 為 function 時,則有另外一種變化。

1
2
3
4
import { chain, add, multiply } from 'ramda';

const fn = chain(multiply, add(1));
console.log(fn(2));

multiply()add() 兩者都是 function,使用 chain() 產生新的 function。

根據 Ramda 對 chain() 的定義:chain(f, g)(x) === f(g(x), x),其中 x 為 data。

也就是 data 會先執行第二個參數的 g(x),並將結果傳入第一個參數 f(x),至於 f(x) 第二個參數仍然是 data。

Data 2 先傳入 add(1) 結果為 3,再將 3 傳入 multiply() 與 data 2 相乘,因此結果為 6

chain001

要注意的是第一個參數 f(x) 要有兩個參數,而 g(x) 為一個參數。

Conclusion


  • chain() 搭配 array 時,可視為 FlatMap(),用來將 nested array 攤平
  • chain() 搭配 function 時,可將 data 先透過其他 function 加工後,再連同 data 傳入主 function

Reference


Fantasy Land, Chain
Ramda, map()
Ramda, flatten()
Ramda, chain()