Point-free 後的 Function 常出現等效重構 Pattern,可再以其他 Function 取代,讓 Codebase 更為精簡。
Version
macOS Catalina 10.15.4
VS Code 1.44.0
Quokka 1.0.285
Ramda 0.27.0
pluck()
import { map, prop } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = k => map(prop(k))
f('price')(data) // ?
若想從 array 只取出特定 property 成為新 array,直覺會使用 map()
與 prop()
組合。
import { pluck } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = pluck
f('price')(data) // ?
map(prop(k))
可等效重構成 pluck(k)
。
project()
import { map, pick } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'RxJS in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS' }
]
let f = map(pick(['title', 'price']))
f(data) // ?
若想從 array 只取出部分 property 成為新 array,直覺會使用 map()
與 pick()
組合。
import { project } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100, category: 'FP' },
{ title: 'RxJS in Action', price: 200, category: 'FRP' },
{ title: 'Speaking JavaScript', price: 300, category: 'JS' }
]
let f = project(['title', 'price'])
f(data) // ?
map(pick([k]))
可等效重構成 project([k])
。
uniq()
import { uniqBy, identity } from 'ramda'
let data = [1, 2, 3, 1]
let f = uniqBy(identity)
f(data) // ?
若想從 array 只取出不重複部分,直覺會使用 uniqBy()
與 identity()
組合。
import { uniq } from 'ramda'
let data = [1, 2, 3, 1]
let f = uniq()
f(data) // ?
uniqBy(identity)
可等效重構成 uniq()
。
tug()
import { pipeK, sum } from 'knex-fp'
import { pluck, head } from 'ramda'
import { log } from 'wink-fp'
pipeK(
sum({ sum: 'price' })
)(
pluck('sum'),
head,
log
)(mySQL('books'))
當使用 Knex 的 aggregate function,如 count()
、sum()
時,最後必須靠 Ramda 的 pluck()
與 head()
組合才能取出值。
import { pipeK, sum } from 'knex-fp'
import { tug, log } from 'wink-fp'
pipeK(
sum({ sum: 'price' })
)(
tug('sum'),
log
)(mySQL('books'))
pipe(pluck(k), head())
的組合可等效重構成 tug(k)
。
sum()
import { reduce } from 'ramda'
let data = [1, 2, 3]
let f = reduce((a, x) => a += x, 0)
f(data) // ?
若想將 array 中的 number 加總,直覺會使用 reduce()
。
import { sum } from 'ramda'
let data = [1, 2, 3]
let f = sum
f(data) // ?
reduce((a, x) => a += x, 0)
可等效重構成 sum()
。
compose()
import { filter } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = v => filter(x => x.price === v)
f(100)(data) // ?
若想將 array 透過 filter()
找出指定資料,會使 f()
傳進 value。
import { filter, useWith, identity, propEq } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = useWith(
filter, [propEq('price'), identity]
)
f(100)(data) // ?
若想將 f()
也 point-free,直覺會使用 useWith()
與 identity()
組合。
import { filter, compose, propEq } from 'ramda'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = compose(filter, propEq('price'))
f(100)(data) // ?
useWith(f, [t, identity])
可等效重構成 compose(f, t)
。
map()
import { filter, propEq } from 'ramda'
import { map } from 'crocks'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 },
{ title: 'Speaking JavaScript', price: 300 }
]
let f = map(filter, propEq('price'))
f(100)(data) // ?
若只有兩個 function 組合,則 compose(f, t)
亦等效於 map(f, t)
。
chain()
import { pipe, map, flatten, inc } from 'ramda'
let data = [[1, 2], [3, 4], [5, 6]]
let f = pipe(
map(map(inc)),
flatten
)
f(data) // ?
若想將 nested array 的 element 都加 1
,然後 flatten 為一層 array,直覺會使用兩層 map()
與 flatten()
組合。
import { pipe, map, flatten, inc, chain } from 'ramda'
let data = [[1, 2], [3, 4], [5, 6]]
let f = chain(map(inc))
f(data) // ?
pipe(map(f), flatten)
可等效重構成 chain(f)
。
import { append, head } from 'ramda'
let data = [1, 2, 3]
let f = a => append(head(a), a)
f(data) // ?
若想使用 append()
對 array 新增 element,且其值由 head()
取得。
import { append, head, converge, identity } from 'ramda'
let data = [1, 2, 3]
let f = converge(append, [head, identity])
f(data) // ?
若想對 f()
point-free,直覺會使用 converge()
組合 append()
、head()
與 identity()
。
import { append, head, chain } from 'ramda'
let data = [1, 2, 3]
let f = chain(append, head)
f(data) // ?
converge(f, [t, identity])
可等效重構成 chain(f, t)
。
chain(f, t)(x) = f(t(x), x)
,當f()
與t()
都需要 data,且f()
的第一個 argument 必須先經過t()
時,也適用於chain()
when()
import { map, ifElse, identity, inc } from 'ramda'
import { isEven } from 'wink-fp'
let data = [1, 2, 3]
let f = map(
ifElse(isEven, inc, identity)
)
f(data) // ?
若想將 array 中 element 是 偶數
則 +1
,奇數
不變,直覺會使用 ifElse()
與 identity()
組合。
import { map, when, inc } from 'ramda'
import { isEven } from 'wink-fp'
let data = [1, 2, 3]
let f = map(
when(isEven, inc)
)
f(data) // ?
ifElse(f, t, identity)
可等效重構成 when(f, t)
。
unless()
import { map, ifElse, identity, inc } from 'ramda'
import { isEven } from 'wink-fp'
let data = [1, 2, 3]
let f = map(
ifElse(isEven, identity, inc)
)
f(data) // ?
若想將 array 中 element 是 奇數
則 +1
,偶數
不變,直覺會使用 ifElse()
與 identity()
組合。
import { map, unless, inc } from 'ramda'
import { isEven } from 'wink-fp'
let data = [1, 2, 3]
let f = map(
unless(isEven, inc)
)
f(data) // ?
ifElse(f, identity, t)
可等效重構成 unless(f, t)
。
Conclusion
- 重構遇到
identity()
時,通常都會有更簡單的 function 可用 chain()
遇到 function 時通常很難一眼認出來,但最少看到converge(f, [t, identity])
要想起chain()
Reference
Ramda, pluck()
Ramda, project()
Ramda, uniq()
Ramda, sum()
Ramda, compose()
Ramda, map()
Ramda, chain()
Ramda, when()
Ramda, unless()