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 大大的文章