點燈坊

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

使用 seq() 同時執行多個 Side Effect

Sam Xiao's Avatar 2020-09-04

Side Effect 在實務上不可避免,若同時要對相同 Value 執行多次 Side Effect 時,標準做法是使用 tap(),可使用 seq() 更精簡。

Version

Wink-fp 1.23.9

tap()

import { pipe, inc, tap } from 'ramda'

let result1 = 0
let result2 = 0

let effectResult1 = x => result1 = x

let effectResult2 = pipe(
  inc, 
  x => result2 = x
)

pipe(
  inc,
  tap(effectResult1),
  tap(effectResult2)
)(1)

result1 // ?
result2 // ?

第 3 行

let result1 = 0
let result2 = 0

第 6 行

let effectResult1 = x => result1 = x

第一個 effectResult() 寫入 result1 side effect。

第 8 行

let effectResult2 = pipe(
  inc, 
  x => result2 = x
)

先執行 inc() 後再寫入 result2 side effect。

13 行

pipe(
  inc,
  tap(effectResult1),
  tap(effectResult2)
)(1)

標準做法是使用兩次 tap() 寫入兩個 side effect。

seq000

seq()

import { pipe, inc, forEach } from 'ramda'

let result1 = 0
let result2 = 0

let effectResult1 = x => result1 = x

let effectResult2 = pipe(
  inc, 
  x => result2 = x
)

let seq = (...fs) => x => (forEach(f => f(x), fs), x)

pipe(
  inc,
  seq(effectResult1, effectResult2)
)(1)

result1 // ?
result2 // ?

13 行

let seq = (...fs) => x => (forEach(f => f(x), fs), x)

seq() 為 variadic function,可接受多個 function 為 argument,因為在 ES6 的 rest parameter 就是 Array,因此可直接使用 forEach() 執行多個 side effect。

最後將 x 回傳使 Function Pipeline 不中斷。

15 行

pipe(
  inc,
  seq(effectResult1, effectResult2)
)(1)

使用 seq() 取代 tap() 只要一行即可。

seq001

Wink-fp

import { pipe, inc } from 'ramda'
import { seq } from 'wink-fp'

let result1 = 0
let result2 = 0

let effectResult1 = x => result1 = x

let effectResult2 = pipe(
  inc, 
  x => result2 = x
)

pipe(
  inc,
  seq(effectResult1, effectResult2)
)(1)

result1 // ?
result2 // ?

Wink-fp 已經內建 seq() 可直接使用。

seq()
((a -> *),...) -> a -> a
同時執行多個 side effect

((a -> *),...):可傳入多的 function 執行 side effect

a:除入任何型別資料

a:回傳該資料以利繼續 Function Pipeline

seq002

Conclusion

  • seq() 也稱為 S Combinator,在 Function Pipeline 中同時執行多個 Side Effect 時特別好用

Reference

Ramda, tap()