Haskell中Either列表的sequence操作类型选择与Left处理规则疑问
问题1:sequence对Either类型的参数选择逻辑
首先明确Haskell中类型类实例的定义规则:sequence 要求作用的元素类型必须是 Applicative 类的实例,而 Applicative 要求对应的类型构造器的kind为 * -> *(也就是只能接收一个类型参数)。
Either本身的kind是 * -> * -> *,需要先填充第一个类型参数才能满足 * -> * 的要求,标准库中Either的Applicative实例固定填充第一个参数为错误类型,实例声明为:
instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r
所以当你有 [Either CmdError MonStat] 类型的列表时,匹配Applicative实例的类型构造器是 Either CmdError,对应sequence签名里的m,MonStat对应签名里的a,自然执行sequence后得到的是 Either CmdError [MonStat],只会把右值MonStat聚合为列表,左值的错误类型CmdError保持不变。
如果你想要聚合所有Left值,需要使用专门做错误累加的Validation类型,或者自己用newtype包装Either实现反向的Applicative实例,标准Either没有提供也不需要提供反向实例,避免语义混淆。
问题2:多Left值的处理逻辑
最终会返回第一个出现的Left值,不会做任何拼接,也不会返回后续的Left。
这个设计是标准Either的「快速失败」语义决定的:从上面的Applicative实例实现就能看到,只要运算链中出现第一个Left,后续的所有运算都会被短路跳过,直接返回这个第一个遇到的错误。这种设计适合不需要收集全部错误、遇到第一个错误就可以终止整个操作的场景,是工业界最常用的错误处理逻辑之一。如果你需要收集所有错误,使用前面提到的Validation类型即可。
内容的提问来源于stack exchange,提问作者craigbot

