點燈坊

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

自行實作 compose() 組合 Function

Sam Xiao's Avatar 2020-03-07

compose() 是 FP 最重要 Function 之一,其中 Function Composition 就是由這此 Function 展開,實務上都是直接使用 Ramda 的 compose(),事實上也能自行以 ECMAScript 的 reduceRight() 實現。

Version

Ramda 0.27.0
Wink-fp 1.20.47

Ramda

import { compose, filter, map } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let f = v => compose(
  map(x => x.title),
  filter(x => x.price === v),
)

f(300)(data) // ?

實務上實現 Function Composition 時,會使用 Ramda 的 compose()

pipe000

reduceRight()

import { filter, map } from 'ramda'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let compose = (...fs) => v => fs.reduceRight((g, f) => f(g), v)

let f = v => compose(
  map(x => x.title),
  filter(x => x.price === v),
)

f(300)(data) // ?

第 1 行

import { filter, map } from 'ramda'

沒使用 Ramda 所提供的 compose()

第 9 行

let compose = (...fs) => v => fs.reduceRight((g, f) => f(g), v)

由於 compose() 是由右至左,因此使用 reduceRight()

(...fs)

使用 ES6 的 rest parameter,表示 compose() 的 argument 個數無限,且 fs 為 array,可大膽使用 Array.prototype 下的 reduceRight()

v => fs.reduceRight((g, f) => f(g), v)

f 為目前 function,g 為組合後 function,每次會執行 f(g) 組合計算結果。

因為結果仍是 function,所以為 init => fns.reduceRight()

pipe001

Wink-fp

import { filter, map } from 'ramda'
import { compose } from 'wink-fp'

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let f = v => compose(
  map(x => x.title),
  filter(x => x.price === v),
)

f(300)(data) // ?

亦可改用 Wink-fp 所提供的 compose(),結果完全相同。

compose()
((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z)
將 function 由右至左組合成新 function

compose002

Conclusion

  • compose() 一直被認為是黑魔法,事實上只是將 function 加以 reduceRight() 而已,能自己寫過一次,就不會覺得那麼遙不可及了

Reference

Yazeed Bzadough, 10 More Utility Functions Made with Reduce
MDN, Rest parameters
MDN, reduceRight()
Ramda, compose()