點燈坊

失くすものさえない今が強くなるチャンスよ

ECMAScript 之 Higher Order Function

Sam Xiao's Avatar 2021-06-06

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。

hof000

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 傳入。

hof001

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 消去

hof002

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 取代。

hof003

Conclusion

  • 由於 ECMAScript 定義 curry function 與呼叫 function 語法不同,當 = 左右兩側的 argument 相同時,不太容易發現並消去
  • 還可發現 curry function 的妙用,對於重構成 higher order function 的直覺與方便,這也是為什麼 FP 預設都使用 curry function