ECMAScript 支援 First Class Function,所以可將各 Function 相同部分抽成 Higher Order Function,不同部分亦抽成小 Function,最後以 Argument 傳入產生各 Function。
Version
ECMAScript 2015
Named Function
let isEven = x => x % 2 === 0
let ifEvenInc = x => isEven(x) ? x + 1 : x
let ifEvenDouble = x => isEven(x) ? x * 2 : x
let ifEvenSquare = x => isEven(x) ? x ** 2 : x
ifEvenInc(4) // ?
ifEvenDouble(4) // ?
ifEvenSquare(4) // ?
我們有 ifEvenInc()
、ifEvenDouble()
與 ifEvenSquare()
三個 function ,都判斷 parameter 是否為偶數,若是則執行不同 expression,否則都回傳原來值。
我們發現這三個 function 非常類似,唯一差別只有 parameter 為偶數時的回傳不同 expression。
Higher Order Function
let isEven = x => x % 2 === 0
let inc = x => x + 1
let double = x => x * 2
let square = x => x ** 2
let ifEven = f => x => isEven(x) ? f(x) : x
let ifEvenInc = x => ifEven(inc)(x)
let ifEvenDouble = x => ifEven(double)(x)
let ifEvenSquare = x => ifEven(square)(x)
ifEvenInc(4) // ?
ifEvenDouble(4) // ?
ifEvenSquare(4) // ?
可將不同 expression 單獨抽出來成為 function,ifEven()
的第一個 argument 以 function 傳入。
Point-free
let isEven = x => x % 2 === 0
let inc = x => x + 1
let double = x => x * 2
let square = x => x ** 2
let ifEven = f => x => isEven(x) ? f(x) : x
let ifEvenInc = ifEven(inc)
let ifEvenDouble = ifEven(double)
let ifEvenSquare = ifEven(square)
ifEvenInc(4) // ?
ifEvenDouble(4) // ?
ifEvenSquare(4) // ?
可發現 ifEvenInc()
與 ifEven()
最後一個 argument 都是 x
,可同時兩邊消去 x
,使 ifEven()
改回傳 function。
只有 curry function 才允許
=
兩邊相同 argument 消去
Anonymous Function
let isEven = x => x % 2 === 0
let ifEven = f => x => isEven(x) ? f(x) : x
let ifEvenInc = ifEven(x => x + 1)
let ifEvenDouble = ifEven(x => x * 2)
let ifEvenSquare = ifEven(x => x ** 2)
ifEvenInc(4) // ?
ifEvenDouble(4) // ?
ifEvenSquare(4) // ?
除了 named function 外,對於只使用一次的 function,亦可使用 arrow function 取代。
Conclusion
- 由於 ECMAScript 定義 curry function 與呼叫 function 語法不同,當
=
左右兩側的 argument 相同時,不太容易發現並消去 - 還可發現 curry function 的妙用,對於重構成 higher order function 的直覺與方便,這也是為什麼 FP 預設都使用 curry function