點燈坊

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

使用 Maybe 取代 undefined

Sam Xiao's Avatar 2021-04-21

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

Version

Vue 3.0.11
Sanctuary 3.1.0

Array.prototype.find

maybe001

輸入書本名稱,若找不到則顯示 N/A

maybe000

若找得到則顯示其 price。

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

<script setup>
let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'Elm in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

ref: title = ''
ref: price = ''

let onClick = () => {
  let result = data.find(x => x.title === title)

  if (result) price = result.price
  else price = 'N/A'
}
</script>

第 8 行

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'Elm in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

資料定義在 data,若輸入 FP in JavaScript,將顯示 100

17 行

let onClick = () => {
  let result = data.find(x => x.title === title)

  if (result) price = result.price
  else price = 'N/A'
}

使用了 ECMAScript 的 Array.prototype.find,若找到則回傳 Object,若找不到則回傳 undefined

由於致命的 undefined,這使得 Method Chaining 中斷,必須回到 Imperative 使用 if 判斷

Maybe

maybe000

結果不變,但使用 Maybe 改寫。

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

<script setup>
import { ref } from 'vue'
import { read, write } from 'vue3-fp'
import { pipe, propEq, map, always } from 'ramda'
import { find, prop, flip, create, env } from 'sanctuary'

let { maybe, I } = create({ checkTypes: false, env })

let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'Elm in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

let title = ref ('')
let price = ref ('')

let onClick = pipe (
  read (title),
  propEq ('title'),
  flip (find) (data),
  map (prop ('price')),
  maybe ('N/A') (I),
  write (price)
)
</script>

24 行

let onClick = pipe (
  read (title),
  propEq ('title'),
  flip (find) (data),
  map (prop ('price')),
  maybe ('N/A') (I),
  write (price)
)

使用 pipe 組合 onClick

  • read (title):讀取 title state
  • propEq ('title') :組合 find 所需 callback
  • flip (find) (data):與 Array.prototype.find 的差異是無論找不找得到都會傳 Maybe,因此不用擔心 undefined 問題
  • map (prop ('price')):從 Just 內讀取出 price
  • maybe ('N/A') (I):若是 Nothing 則回傳 N/A,Just 則回傳 Maybe 內部值
  • write (price):最後寫入 price state

可發現 Maybe 不必在商業邏輯中間使用 if 中斷,全部過程都是 happy path,只要在最後寫入 side effect 前以 maybe 從 Maybe 內取出值即可

Conclusion

  • 當使用 Sanctuary 的 find 時,若要搭配 flip,記得要使用 Sanctuary 的 flip,而不能使用 Ramda 的 flip,否則無法真正確翻轉 find 的 argument
  • 可發現由於 Sanctuary 的 find 回傳 Maybe,因此很平順的在 Function Pipeline 使用,完全不用搭配 Imperative 的 if 做判斷,完全發揮 Vue 3 的 Composition API 優點

Reference

Sanctuary, find
Sanctuary, Maybe