點燈坊

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

使用 Either 處理 Exception

Sam Xiao's Avatar 2021-04-21

由於 Vue 3 支援 Composition API,不只 Function Pipeline 與 Point-free 得以實現,如 FP 慣用的 Either 在 Vue 3 也得以發揮。

Version

Vue 3.0.11
Sanctuary 3.1.0

Either

either000

輸入一般值會計算出結果。

either001

輸入 0 時會直接顯示 Exception 訊息。

<template lang='pug'>
input(v-model='num')
button(@click='onClick') Submit
span {{ result }}
</template>

<script setup>
import { ref } from 'vue'
import { read, write } from 'vue3-fp'
import { pipe } from 'ramda'
import { Left, Right, map, add, either, I } from 'sanctuary'

let num = ref(0)
let result = ref('')

let div = x => y =>
  y === 0 ?
  Left('Can not divide by zero') :
  Right(x / y)

let onClick = pipe(
  read(num),
  div(2),
  map(add(1)),
  either(I)(I),
  write(result)
)
</script>

16 行

let div = x => y =>
  y === 0 ?
  Left('Can not divide by zero') :
  Right(x / y)

自行實作 div(),當 /0 時回傳 Left Either,正常結果則回傳 Right Either。

21 行

let onClick = pipe(
  read(num),
  div(2),
  map(add(1)),
  either(I)(I),
  write(result)
)

使用 pipe() 組合出 onClick()

  • read(num):讀取 num state
  • div(2):計算除法
  • map(add(1)):使用 add() 計算加法,因為 div() 回傳 Either,所以 add() 須在 map()
  • either(I)(I):使用 either(I)(I) 取出 Either 內部值
  • write(result):寫入 result state

可發現 div() 雖然可能發出 Exception,但不會因此中斷 Function Pipeline,且後續計算都可維持在 Either 內,只要最後從 Either 內部取出值即可

Promise

either000

結果不變,但使用 Promise 模擬 Either 改寫。

<template lang='pug'>
input(v-model='num')
button(@click='onClick') Submit
span {{ result }}
</template>

<script setup>
import { ref } from 'vue'
import { read, write } from 'vue3-fp'
import { pipe, andThen as then, otherwise, add } from 'ramda'
import { reject, resolve } from 'wink-fp'

let num = ref(0)
let result = ref('')

let div = x => y =>
  y === 0 ?
  reject('Can not divide by zero') :
  resolve(x / y)

let onClick = pipe(
  read(num),
  div(2),
  then(add(1)),
  then(write(result)),
  otherwise(write(result))
)
</script>

16 行

let div = x => y =>
  y === 0 ?
  reject('Can not divide by zero') :
  resolve(x / y)

將 Exception 訊息改包在 Rejected Promise 內,正常值則包在 Fulfilled Promise。

21 行

let onClick = pipe(
  read(num),
  div(2),
  then(add(1)),
  then(write(result)),
  otherwise(write(result))
)

使用 pipe() 組合出 onClick()

  • read(num):讀取 num state
  • div(2):計算除法
  • then(div(1)):使用 add() 計算加法,因為 div() 回傳 Promise,所以 add() 須在 then()
  • then(write(result)):取出 Resolved Promise 並寫入 result state
  • otherwise(write(result)):取出 Rejected Promise 並寫入 result state

使用 Promise 取代 Either 後,後續 function 都必須在 then() 內,且最後使用 then() 取出 Resolved Promise,使用 otherwise() 取出 Rejected Promise

Conclusion

  • div() 使用傳統 Exception,則無法在 Function Pipeline 使用,必須回到 Imperative 寫法
  • 若改用 Either 或 Promise 回傳 Exception,配合 Vue 3 的 Composition API 則可順利以 Point-free 組合出 onClick()
  • 雖然可以使用 Promise 模擬 Either,但實務上還是建議直接使用 Either 語意較佳

Reference

Sanctuary, Either