實務上在檢查時一定會同時檢查多個條件,此時必須使用 chain
搭配 Either。
Version
Vue 3.0.11
Sanctuary 3.1.0
Composition API
按下 Submit
會檢查 password1
是否為空值。
按下 Submit
會檢查 password2
是否為空值。
還會檢查 password1
與 password2
是否相等。
若通過檢查則顯示 Your password is xxx
。
可發現
Submit
一共檢查了 3 個條件
<template lang='pug'>
div
span Password1:
input(v-model='password1')
div
span Password1:
input(v-model='password2')
div
button(@click='onClick') Submit
div {{ msg }}
</template>
<script setup>
ref: password1 = ''
ref: password2 = ''
ref: msg = ''
let onClick = () => {
if (password1 === '') {
msg = 'Password1 is empty'
return
}
if (password2 === '') {
msg = 'Password2 is empty'
return
}
if (password1 !== password2) {
msg = 'Password is not identical'
return
}
msg = `Your password is ${password1}`
}
</script>
18 行
let onClick = () => {
if (password1 === '') {
msg = 'Password1 is empty'
return
}
if (password2 === '') {
msg = 'Password2 is empty'
return
}
if (password1 !== password2) {
msg = 'Password is not identical'
return
}
msg = `Your password is ${password1}`
}
Imperative 會使用 Guard Clause,也就是每個 if
檢查若失敗則提早 return
,避免使用 else
。
Either
結果不變,但使用 Either 改寫。
<template lang='pug'>
div
span Password1:
input(v-model='password1')
div
span Password2:
input(v-model='password2')
div
button(@click='onClick') Submit
div {{ msg }}
</template>
<script setup>
import { ref, unref } from 'vue'
import { read, write } from 'vue3-fp'
import { pipe, isEmpty, ifElse, thunkify, map, chain, concat } from 'ramda'
import { Left, Right, either, I } from 'sanctuary'
let password1 = ref ('')
let password2 = ref ('')
let msg = ref ('')
let chkEmpty = field => ifElse (
isEmpty,
thunkify (Left) (`${field} is empty`),
Right
)
let chkIdentical = ifElse (
x => unref (password2) === x,
Right,
thunkify (Left) ('Password is not identical')
)
let onClick = pipe (
read (password1),
chkEmpty ('Password1'),
map (read (password2)),
chain (chkEmpty ('Password2')),
map (read (password1)),
chain (chkIdentical),
map (concat ('Your password is ')),
either (I) (I),
write (msg)
)
</script>
35 行
let onClick = pipe (
read (password1),
chkEmpty ('Password1'),
map (read (password2)),
chain (chkEmpty ('Password2')),
map (read (password1)),
chain (chkIdentical),
map (concat ('Your password is ')),
either (I) (I),
write (msg)
)
使用 pipe
組合出 onClick
:
read (password1)
:讀取password1
statechkEmpty ('Password1')
:檢查password1
state 是否為空值,無論結果如何都會回傳 Either,避免為空值時繼續後續運算map (read (password2))
:繼續讀取password2
state,由於前一個 function 回傳 Either,因此read
必須包在map
內chain (chkEmpty ('Password2'))
:繼續檢查password2
state 是否為空值,無論結果如何都會回傳 Either,但因為已經在 Either 內,chkEmpty
再回傳 Either 會造成兩層 Either,因此使用chain
攤平成為一層 Eithermap (read (password1))
:繼續讀取password1
state,由於前一個 function 回傳 Either,因此read
必須包在map
內chain (chkIdentical)
:繼續檢查password1
與password2
是否相等,無論結果如何都會回傳 Either,但因為已經在 Either 內,chkIdentical
再回傳 Either 會造成兩層 Either,因此使用chain
攤平成為一層 Eithermap (concat ('Your password is '))
:若通過檢查則顯示Your password is xxx
either (I) (I)
:從 Left Either 與 Right Either 內取出值write (msg)
:最後寫入msg
state
23 行
let chkEmpty = field => ifElse (
isEmpty,
thunkify (Left) (`${field} is empty`),
Right
)
使用 ifElse
組合出 chkEmpty
:
isEmpty
:檢查是否為空值thunkify (Left) ()
:若為空值則回傳 Left EitherRight
:若不是空值則回傳 Right Either
29 行
let chkIdentical = ifElse (
x => unref (password2) === x,
Right,
thunkify (Left) ('Password is not identical')
)
使用 ifElse
組合出 chkIdentical
:
x => unref(password2) === x
:檢查是否相等Right
:若相等則回傳 Right Eitherthunkify (Left) ('Password is not identical')
:若不相等則回傳 Left Either
Conclusion
- 可發現若只有一個檢查條件回傳 Either,則後續的運算都要包在
map
內;但若有多個條件都回傳 Either,則後續的運算先使用chain
攤平成一層 Either,然後才能使用map
套用其他 function