很多人覺得 Closure 與 Currying 太過學術,實務上很少用到,事實上 Closure 與 Currying 搭配 Promise Chain 幾乎天天都會使用。
Version
Ramda 0.27.1
Closure && Currying
import { pipe, andThen as then, otherwise, find } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'Programming Haskell', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = async () => data
let g = price => a => find(x => x.price === price, a)
pipe(
f,
then(g(100)),
otherwise(console.error)
)() // ?
第 9 行
let f = async () => data
f()
回傳為 Promise,若要再使用 g()
,勢必放在 then()
內。
第 10 行
let g = price => a => find(x => x.price === price, a)
原本 g()
應為
let g = a => find(x => x.price === price, a)
如此 g()
為標準 function,可為 then()
接受。
但可惜我們仍需傳入 price
方能使用 x.price === price
。
let g = (price, a) => find(x => x.price === price, a)
傳統寫法會再多一個 price
argument,如此雖然可行,但 g()
很難整進 then()
內。
let g = price => a => find(x => x.price === price, a)
若將 (price, a)
改以 currying 方式,則透過 closure 將得以保存 price
,且回傳的 a => find(x => x.price === price, a)
也符合 then()
所需 signature。
12 行
pipe(
f,
then(g(100)),
otherwise(console.error)
)() // ?
如此 g(100)
則可順利整進 then()
內。
Point-free
import { pipe, andThen as then, otherwise, find } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'Programming Haskell', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = async () => data
let g = price => find(x => x.price === price)
pipe(
f,
then(g(100)),
otherwise(console.error)
)() // ?
第 10 行
let g = price => find(x => x.price === price)
由於 g()
最後 argument 為 Array,可將 a
去除使其 Point-free。
import { pipe, andThen as then, otherwise, find, propEq } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'Programming Haskell', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = async () => data
let g = price => find(propEq('price', price))
pipe(
f,
then(g(100)),
otherwise(console.error)
)() // ?
10 行
let g = price => find(propEq('price', price))
find()
的 callback 可進一步使用 propEq()
使其 Point-free。
import { pipe, andThen as then, otherwise, find, propEq } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'Programming Haskell', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = async () => data
let g = pipe(propEq('price'), find)
pipe(
f,
then(g(100)),
otherwise(console.error)
)() // ?
第 10 行
let g = pipe(propEq('price'), find)
也可直接以 pipe()
組合 propEq()
與 find()
,如此 g()
完全 Point-free。
Conclusion
- Closure 與 currying 技巧在 Promise Chain 中經常使用,如此可發現 function 在
then()
中不再只是單純 callback 角色,而是以 pure function 扮演運算橋樑 - 本文重點是以 closure 與 currying 產生適合
then()
的 callback,至於後續 refactoring 則為加分題,只有在先以 closure 與 currying 建立 function 條件下,才有後續的化簡