ECMAScript 2015 提供了新的 Array.prototype.fill()
,唯其以 Method Chianing 形式提供,且有 Side Effect 問題,Wink-fp 特別提供 Function 化的 fill()
解決此問題。
Version
macOS Catalina 10.15.4
VS Code 1.43.2
Quokka 1.0.284
Ramda 0.27.0
ECMAScript
let data = [1, 2, 3]
data.fill(0, 1, 3) // ?
ES6 的 fill()
雖然好用,但卻有幾個隱憂:
- Data 並非最後一個 argument,因此無法 point-free 與 function composition
- Argument 並非 currying 形式,因此無法 partial application
fill()
有 side effect,會影響原本 function 外的 array
其中第三點是其致命傷,一不小心就是 bug 來源。
fill()
import { clone } from 'ramda'
let data = [1, 2, 3]
let fill = v => begin => end => a => clone(a).fill(v, begin, end)
fill(0)(1)(3)(data) // ?
data // ?
可自行建立 fill()
,將 array 擺到最後一個 argument,且其他 argument 都以 currying 形式提供。
先使用 clone()
斷開原本 array,如此就可安心使用 fill()
不用擔心 side effect。
Wink-fp
import { fill } from 'wink-fp'
let data = [1, 2, 3]
fill(1)(3)(0)(data) // ?
data // ?
Wink-fp 已經提供 fill()
可直接使用。
fill()
a -> Number -> Number -> [a] -> [a]
一次改變 array 內多個 element
a
:要改變的值
Number
:起始 index
Number
:結束 index
[a]
:data 為 array
[a]
:回傳改變後的 array
可發現原本 array 沒有被修改。
Application
import { concat, pipe, curry } from 'ramda'
import { fill } from 'wink-fp'
let data0 = [1, 2, 3]
let data1 = [4, 5, 6]
let fillAll = fill(0)(Infinity)
let f = v => curry(pipe(
concat,
fillAll(v)
))
f(0)(data0)(data1) // ?
Function 化的 fill()
有什麼用呢 ? 它可輕易與其他 function 做 function composition。
若我們想將兩個 array 合併之後,並提供初始值一併初始化。
可先使用 fill(0)(Infinity)
產生 fillAll()
,再使用 pipe()
組合 concat()
與 fillAll()
。
Point-free
import { concat, useWith, identity } from 'ramda'
import { fill } from 'wink-fp'
let data0 = [1, 2, 3]
let data1 = [4, 5, 6]
let fillAll = fill(0)(Infinity)
let f = useWith(
fillAll, [identity, concat]
)
f(0)(data0)(data1) // ?
也可使用 useWith()
使 f()
進一步 point-free。
Conclusion
- Wink-fp 的
fill()
不是單純使用invoker()
將 method 轉成 curried function 而已,還順便解決其 side effect 問題 - Function 化的
fill()
可輕易與其他 function 做組合
Reference
MDN, Array.prototype.fill()
Ramda, concat()
Ramda, pipe()
Ramda, curry()
Ramda, useWith()
Ramda, identity()