代表多个失败案例但只有一个成功案例的惯用类型是什么?

时间:2018-04-27 20:48:40

标签: haskell idioms

类型Maybe a表示可能失败的计算,其语义我们并不关心如何失败。如果计算成功,则可能返回任何类型a的值。

相反的情况如何,计算可能由于多种原因而失败(我们希望保留该信息)但成功不涉及除“是,它成功”之外的任何信息?我可以想到两种明显的方法来编码这种计算:

  • Maybe e,其中Just e表示失败,Nothing表示成功。这与Maybe的通常用法相反,我不愿意使用它。
  • Either e (),其中Left e表示失败,Right ()表示成功。这具有明确的优点,但具有......明确的缺点。写()感觉很尴尬,特别是在类型签名的上下文之外。

Haskell是否有更惯用的方式来表示“多个失败案例,但只有一个成功案例”?

3 个答案:

答案 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的优势在于已拥有所有现有机制:FunctorApplicativeMonad,{{1 }},FoldableTraversable甚至SemigroupOrd应始终保持)实例可能已经完全按照您的错误处理方式运行。通常,对于这种特殊情况,Left x < Right y往往会与您想要的相反(通常您希望继续成功并在第一次失败后停止,这与大多数{{1}相反机器将提供这种情况)。