當有 Function 回傳 Maybe,而後續的 Function 都在 Maybe 內執行時,卻又有 Function 回傳 Promise,這就造成 Maybe 內有 Promise 的窘境,實務上該如何處理這種兩層 Monad 呢 ?
Version
Vue 3.0.11
Sanctuary 3.1.0
Promise Maybe
當輸入的書名找不到時,會顯示 N/A
。
當找到書名時,就會繼續呼叫 API 查詢價錢。
當 API 回傳 error response 時,則顯示 error message。
<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, andThen as then, otherwise, path } from 'ramda'
import { create, env, prop, flip, find } from 'sanctuary'
import { resolve } from 'wink-fp'
import getPrice from '/src/api/getPrice'
let { fromMaybe } = create({ checkTypes: false, env })
let data = [
{ id: 1, title: 'FP in JavaScript' },
{ id: 2, title: 'Elm in Action' },
{ id: 3, title: 'Speaking JavaScript' }
]
let title = ref('')
let price = ref('')
let onClick = pipe(
read(title),
propEq('title'),
flip(find)(data),
map(prop('id')),
map(getPrice),
map(then(path(['data', 'price']))),
fromMaybe(resolve('N/A')),
then(write(price)),
otherwise(path(['response', 'data', 'error'])),
then(write(price))
)
</script>
17 行
let data = [
{ id: 1, title: 'FP in JavaScript' },
{ id: 2, title: 'Elm in Action' },
{ id: 3, title: 'Speaking JavaScript' }
]
使用
find()
查詢title
,無論是否存在都會回傳 Maybe後續的處理也都會在 Maybe 內,包含呼叫 API 也會在 Maybe 內
API 會回傳 Promise,這就造成 Promise 在 Maybe 內,也就是 Promise Maybe
該如何處理 Promise Maybe 這種兩層 Monad 呢 ?
26 行
let onClick = pipe(
read(title),
propEq('title'),
flip(find)(data),
map(prop('id')),
map(getPrice),
map(then(path(['data', 'price']))),
fromMaybe(resolve('N/A')),
then(write(price)),
otherwise(path(['response', 'data', 'error'])),
then(write(price))
)
使用 pipe()
組合 onClick()
:
read(title)
:讀取title
statepropEq('title')
:組合find()
所需要的 callbackflip(find)(data)
:與Array.prototype.find()
的差異是無論找不找得到都會傳 Maybe,因此不用擔心undefined
問題map(prop('id'))
::在 Maybe 內讀取出id
propertymap(getPrice)
:呼叫getPrice()
從 API 查詢 price,會回傳 Promisemap(then(path(['data', 'price'])))
:關鍵在此行,Promise 在 Maybe 內,因此只能map()
先拆第一層 Maybe,再用then()
拆第二層 Promise,所有的 function 都在map(then)
內fromMaybe(resolve('N/A'))
:處理 Nothing Maybe,將N/A
包成 Promise 回傳then(write(price))
:處理 Resolved Promise,最後寫入price
stateotherwise(path(['response', 'data', 'error']))
:從 Rejected Promise 內取出e.response.data.error
並回傳 Promisethen(write(price))
:將error
寫入price
state
Conclusion
- Promise Maybe 乍聽很嚇人,其實只要把握用
map()
或chain()
處理 Maybe,用then()
處理 Promise,然後將所有 function 處理都寫在map(then())
內即可