Haskell中如何简洁实现Either类型短路列表校验函数
结论
- 该短路列表校验操作属于标准的monadic操作:核心短路能力完全依托
Either e类型的Monad实例实现——只要单次校验返回Left错误,就会立刻终止后续所有计算、直接返回错误,完全符合Monad串联带上下文计算的行为特征。 - 存在远比重写嵌套case更简洁的实现方式,且完全可以通过
>>=运算符改写原有递归逻辑。
使用
>>=的改写实现 你手写的嵌套模式匹配逻辑,本质就是在Either e的Monad上下文里串联校验步骤,>>=运算符本身就内置了Left分支的短路处理逻辑,不需要手动写case匹配,改写后代码如下:
validateList :: (a -> Either e a) -> [a] -> Either e [a] validateList _ [] = Right [] validateList validate (x:xs) = validate x >>= \y -> validateList validate xs >>= \ys -> Right (y:ys)
如果偏好可读性更高的语法糖,等价的do notation写法和上述逻辑完全一致(do块最终会被编译为>>=的嵌套调用):
validateList :: (a -> Either e a) -> [a] -> Either e [a] validateList _ [] = Right [] validateList validate (x:xs) = do y <- validate x ys <- validateList validate xs pure (y:ys)
最简洁的通用实现
你提到的逻辑点完全正确:此处列表[]并非作为Monad参与计算,而是作为Traversable类型类的实例使用。Haskell标准库中traverse函数的类型签名为Traversable t => (a -> f b) -> t a -> f (t b),当t取[]、f取Either e时,类型和行为完全匹配当前的校验需求:按顺序遍历列表元素执行校验函数,遇到错误立刻短路返回,全部校验通过则返回包装好的结果列表。
因此最终极简实现只需要一行,和手写递归、>>=改写版本的行为完全等价:
validateList :: (a -> Either e a) -> [a] -> Either e [a] validateList = traverse
补充说明:
traverse本身只要求上下文f是Applicative实例,不需要强Monad约束,Either e的Applicative实例同样实现了Left短路逻辑,因此行为完全符合预期。如果列表作为Monad参与计算,会执行非确定性的多结果组合逻辑,反而和当前单错误短路的需求不符。
内容的提问来源于stack exchange,提问作者gust
相关产品推荐
相关产品推荐

