export const textInputOf =
({ minLen, maxLen }: { minLen: number; maxLen: number }) =>
(input: string) =>
pipe(
input,
validateMinLen(minLen),
Either.flatMap(validateMaxLen(maxLen)),
Either.flatMap(validateWhiteList),
// 以下兩行先忽略
// Either.mapRight(ofValid),
// Either.merge
)
回顧昨天的驗證程式,如果用 if else 寫會是這樣
const textInputOf = ({ minLen, maxLen }: { minLen: number; maxLen: number }) =>
(input: string): InvalidTextInput | ValidTextInput => {
const either1 = validateMinLen(minLen)(input)
if(either1._tag==='Right'){
const input2 = either1.right
const either2 = validateMaxLen(maxLen)(input2)
if(either2._tag==='Right'){
const input3 = either2.right
const either3 = validateMaxLen(maxLen)(input3)
if(either2._tag==='Right'){
return either3
}else{
return either2
}
}else{
return either2
}
}else{
return either1
}
}
其中導致程式變成巢狀,而且一直重複出現的邏輯就是 Either flatMap
,
它由 mapRight
和 flatten
組成
Either mapRight
想像Either
是一個型別盒子,裡面可能有是Right
也可能是Left
。
如果盒子裡面是Right
就把右值取出放入一個能產生任意型別的 callback function,再把這個 function 的回傳值Output2
包裝進Either
。
如果盒子裡面是Left
就回傳Either
。
有時候剛好 callback function 的輸出也是一個 Either
盒子
這時候就要再加上一個 flatten
的操作
Either flatten
把重複兩層的其中一層盒子拆掉,保留值在同一層盒子裡
把 mapRight
和flatten
串起來就會變成這樣
再把中間轉換的部份省略就得到了 flatMap
Effect flatMap
mapRight + flatten
在 Either
盒子中,Output
被包在 Right
裡面, Error
被包在 Left
裡面
當我們不需要區分 Left
或 Right
了,我們就可以把兩邊結果合併起來
以 textInputOf
為例,經過 mapRight
處理後 Either
的左右值分別是 InvalidTextInput
和 ValidTextInput
。這兩個型別都有 tag,可以合併成一個 sum type
,且合併後的 sum type 也是有意義的,因此我們就可以把 Either
盒子拆掉,把最終結果 InvalidTextInput | ValidTextInput
合在一起拿出來。
把滑鼠移上去看型別在是一種相當好用的 debug 技巧
對型別有疑問? 滑鼠移上去就對了 !
最後要強調的是,Either
只是其中一種資料盒子,還有很多種盒子也有 map
、flatMap
等概念。這些操作具體會做甚麼跟盒子類型有關。如果你想對資料型別盒子有更多瞭解,推薦參考 hanna 大大的文章