thunkify()
在 Ramda 算較罕用的 Function,若搭配能接受 Thunk 的 API 或準備 Function 為參數,thunkify()
就能發揮功用。
Version
Ramda 0.27.1
thunkify()
import { thunkify, add } from 'ramda'
let f = thunkify(add)
let g = (x, y) => () => add(x, y)
thunkify(add)(1,2)() // ?
f(1, 2)() // ?
原本 add()
的 signature 為 Number -> Number -> Number
,當使用 Ramda 的 thunkify()
後,會變成 Number -> Number -> () -> Number
。
f
使用thunkify(add)
產生g
則自行建立,f
與g
完全相同
可發現原本 add()
可直接求值,但 f
與 g
都必須透過 ()
才能有結果。
thunkify()
(a, b, ..., j) -> k) -> (a, b, ..., j) -> () -> k)
將原本結果多墊一層()
才執行
(a, b, ..., j) -> k)
:原 function
(a, b, ..., j) -> () -> k)
:signature 都不變,但多墊了一層 ()
fromMaybe()
import { pipe, multiply, map } from 'ramda'
import { create, env } from 'sanctuary'
let { head, fromMaybe } = create ({ checkTypes: true, env })
let data = []
let fib = n => n <= 1 ? n : fib (n - 2) + fib (n - 1)
pipe(
head,
map(multiply(2)),
fromMaybe(fib(30))
)(data) // ?
10 行
pipe(
head,
map(multiply(2)),
fromMaybe(fib(30))
)(data) // ?
使用 pipe()
組合 IIFE:
head
:回傳 Array 第一筆資料,但包在 Maybe 內map(multiply(2))
:對 Maybe 內值乘以2
fromMaybe(fib(30))
:從 Maybe 內取出值,若為 Nothing 則為fib(30)
fromMaybe()
a -> Maybe a -> a
從 Maybe Monad 內部取出值
a
:提供 Nothing 的預設值
Maybe a
:data 為 Maybe
a
:回傳 Maybe 內部值
計算 Fibonacci Number 是很耗 CPU 運算,若沒遇到 Nothing,則 fib(30)
就白浪費 CPU 了。
比較理想的方式是傳入 () -> fib(30)
,因為是 function,因此 fib(30)
不必求值,等 Nothing 出現時才計算 fib(30)
,可避免 CPU 無效運算。
fromMaybe_()
import { pipe, multiply, map, thunkify } from 'ramda'
import { create, env } from 'sanctuary'
let { head, fromMaybe_ } = create ({ checkTypes: true, env })
let data = []
let fib = n => n <= 1 ? n : fib (n - 2) + fib (n - 1)
pipe(
head,
map(multiply(2)),
fromMaybe_(thunkify(fib)(30))
)(data) // ?
13 行
fromMaybe_(thunkify(fib)(30))
Sanctuary 另外提供了 frommMaybe_()
支援 Thunk,可用來避免 CPU 無效運算,使用 thunkify()
將 fib()
轉成 Thunk。
fromMaybe_()
(() -> a) -> Maybe a -> a
從 Maybe Monad 內部取出值,但支援 Thunk
() -> a
:為 Nothing 的預設值提供 Thunk
Maybe a
:data 為 Maybe
a
:回傳 Maybe 內部值
fromMaybe()
與 fromMaybe_()
的差異在於第一個 argument 為 () -> a
,也就是 Thunk,因此 fib(x)
將不會事先運算好才傳進 fromMaybe_()
,而是只將 () -> a
傳進 fromMaybe_()
,因為是 function 不會立即計算 Fibonacci Number,直到真的遇到 Nothing 時才會計算。
onMounted()
另外一個常用 thunkify()
的場景是在 Function Pipeline 下,以 function 為參數傳入下一個 function,如 Vue 的 onMounted()
、watchEffect()
。
<template>
{{ price }}
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { pipe, add, thunkify } from 'ramda'
import { read, write } from 'vue3-fp'
let price = ref(100)
let mount = pipe(
read(price),
add(20),
write(price),
)
pipe(
thunkify(mount),
onMounted
)()
</script>
12 行
let mount = pipe(
read(price),
add(20),
write(price),
)
使用 pipe()
組合 mount()
:
read(price)
:讀取price
stateadd(20)
:加上20
write(price)
:寫入price
state
18 行
pipe(
thunkify(mount),
onMounted
)()
使用 pipe()
組合 IIFE:
thunkify(mount)
:準備mount()
傳入onMounted()
onMounted
:將mount()
掛上mounted
hook
Conclusion
- 只要是很耗 CPU 或耗 memory 的運算,使用 Thunk 的 lazy evaluation 特性可大幅增進執行效率
thunkify()
另外一個常用場合是在 Function Pipeline 下,準備另外一個 function 為參數傳入下一個 function,但 Vue 3 在少數 function 使用thunkify()
會喪失 reactivity,如guard()
只能使用always()
,但如onMounted()
、watchEffect()
與computed()
都可使用thunkify()
或always()