类型Maybe a
表示可能失败的计算,其语义我们并不关心如何失败。如果计算成功,则可能返回任何类型a
的值。
相反的情况如何,计算可能由于多种原因而失败(我们希望保留该信息)但成功不涉及除“是,它成功”之外的任何信息?我可以想到两种明显的方法来编码这种计算:
Maybe e
,其中Just e
表示失败,Nothing
表示成功。这与Maybe
的通常用法相反,我不愿意使用它。Either e ()
,其中Left e
表示失败,Right ()
表示成功。这具有明确的优点,但具有......明确的缺点。写()
感觉很尴尬,特别是在类型签名的上下文之外。Haskell是否有更惯用的方式来表示“多个失败案例,但只有一个成功案例”?
答案 0 :(得分:2)
如果没有看到实际的代码,实际上很难理解你的失败意味着什么。如果它是一个纯函数,那么我不知道使用Maybe
会有什么问题。我从未真正看到Nothing
失败,但同样如此:没有。根据上下文,我要么返回Nothing,要么使用默认值并继续。我知道它可以被视为失败,但更多地取决于观点
如果调用者比函数本身。
现在,您想要表示可能失败但不返回任何内容的计算。如果它是一个纯粹的功能,那是没有意义的。你的功能是纯粹的,没有任何事情发生(没有副作用)而且你没有得到结果。因此,如果成功,你实际上没有计算任何东西:那不成功,没有什么。 ATHI如果你失败了,你就有理由失败了。这与返回Maybe
的简单检查没有什么不同。
例如,您可能需要检查域是否不在黑名单中。为此你在列表中进行查找:没有任何意味着它很好,即使它意味着它从你的观点和失败,并需要停止计算。相同的代码可用于检查您的域属于白名单。在这种情况下,没有什么是失败的:只取决于背景。
现在,如果你正在运行一个monadic动作(比如保存一个文件或其他东西),那么返回什么是有意义的,但是可能发生不同的故障(磁盘已满,路径不正确等)。我们不关心结果的IO的标准签名是IO ()
,因此您可以选择IO (Either e ())
(每个人都能理解)或转到IO ()
并且引发异常(如果它们真的是例外)。
答案 1 :(得分:1)
从计算可能失败的问题中不清楚。
如果它类似于编译器,可能会产生大量错误消息(而不是在第一个上停止),那么你需要类似的东西:
type MyResult a = Either [Error] a
要么结果成功,要么失败并列出原因。
另一方面,如果您有一个非确定性计算,其中每个变体可能成功或失败,那么您需要更多类似的东西:
type MyResult a = [Either Error a]
然后搜索结果列表。如果你找到了一个Right然后返回它,否则组装Lefts列表。
答案 2 :(得分:1)
一个简单的方法是使用Either e ()
和pattern synonym
pattern Success :: Either e () -- Explicit type signature not necessary
pattern Success = Right ()
如果它提高了可读性,您还可以包含其他一些内容,例如
type FailableWith e = Either e ()
pattern FailedWith :: e -> FailableWith e
pattern FailedWith x = Left x
与Maybe
不同,Either
的优势在于已拥有所有现有机制:Functor
,Applicative
,Monad
,{{1 }},Foldable
,Traversable
甚至Semigroup
(Ord
应始终保持)实例可能已经完全按照您的错误处理方式运行。通常,对于这种特殊情况,Left x < Right y
往往会与您想要的相反(通常您希望继续成功并在第一次失败后停止,这与大多数{{1}相反机器将提供这种情况)。