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()
。
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()
。
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
Conclusion
compose()
一直被認為是黑魔法,事實上只是將 function 加以reduceRight()
而已,能自己寫過一次,就不會覺得那麼遙不可及了
Reference
Yazeed Bzadough, 10 More Utility Functions Made with Reduce
MDN, Rest parameters
MDN, reduceRight()
Ramda, compose()