Haskell 什么&x2019;是否将惯用类型表示多个失败案例,但仅表示一个成功案例?

Haskell 什么&x2019;是否将惯用类型表示多个失败案例,但仅表示一个成功案例?,haskell,idioms,Haskell,Idioms,类型可能a表示可能失败的计算,语义上我们并不关心它到底是如何失败的。如果计算成功,则可能返回a类型的任何值 相反的情况呢,计算可能会因为各种原因而失败(我们希望保留这些信息),但成功不涉及除“是的,它成功了”以外的任何信息?我可以想出两种明显的方法来编码这种计算: 可能是e,其中只有e表示失败,没有表示成功。这与通常使用的Maybe完全相反,因此我不愿意使用它 e(),其中左e表示失败,右()表示成功。这有明确的优点,但也有明确的缺点。编写()感觉很尴尬,尤其是在类型签名的上下文之外 Has

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

相反的情况呢,计算可能会因为各种原因而失败(我们希望保留这些信息),但成功不涉及除“是的,它成功了”以外的任何信息?我可以想出两种明显的方法来编码这种计算:

  • 可能是e
    ,其中
    只有e
    表示失败,
    没有
    表示成功。这与通常使用的
    Maybe
    完全相反,因此我不愿意使用它
  • e()
    ,其中
    左e
    表示失败,
    右()
    表示成功。这有明确的优点,但也有明确的缺点。编写
    ()
    感觉很尴尬,尤其是在类型签名的上下文之外

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

如果没有看到实际代码,实际上很难理解失败的含义。如果它是一个纯函数,那么我不认为使用
会有什么问题。我从来没有真正认为什么都不是失败,但事实就是:什么都不是。根据上下文,我要么不返回任何内容,要么使用默认值并继续。我明白这可以被视为失败,但更多的取决于观点
如果调用方比函数本身更重要。
现在,您想要表示一个可能失败但不返回任何结果的计算。如果它是一个纯函数,那就没有意义了。你的功能是纯粹的,没有发生任何事情(没有副作用),你没有得到一个结果。所以在成功的情况下,你实际上什么都没计算:那不是成功,那什么都没有。如果你失败了,你就有了失败的原因。这与返回
可能
的简单检查没有什么不同

例如,您可能需要检查某个域是否不在黑名单中。为此,您在列表中进行查找:没有任何东西意味着它是好的,即使这意味着它是从您的角度和失败的角度出发的,需要停止您的计算。相同的代码可用于检查您的域是否属于白名单。在这种情况下,没有什么是失败的:这取决于上下文


现在,如果您正在运行一元操作(如保存文件或其他内容),则只返回可能发生的不同故障(磁盘已满、路径不正确等)是有意义的。我们不关心结果的IO的标准签名是
IO()
,因此您可以选择
IO(e())
(每个人都会理解),或者选择
IO()
,并引发异常(如果它们真的异常)

从这个问题上看,计算如何失败还不清楚

如果它类似于编译器,可能会产生大量错误消息(而不是在第一条消息上停止),那么您需要类似以下内容:

type MyResult a = Either [Error] a
它要么以结果成功,要么以一系列原因失败

另一方面,如果您有一个非确定性的计算,其中每个变化可能成功或失败,那么您需要更像:

type MyResult a = [Either Error a]

然后搜索结果列表。如果你找到一个右键,然后返回它,否则就组合左键列表。

一个简单的方法是将
e()

如果它提高了可读性,您还可以包括一些其他内容,例如

type FailableWith e = Either e ()

pattern FailedWith :: e -> FailableWith e
pattern FailedWith x = Left x

Maybe
不同,
都有一个优点,即所有现有的机制都已就位:
函子
应用
单子
可折叠
可遍历
半群
,甚至
Ord
左x<右y
应该始终保持不变)实例可能已经完全按照您希望的方式进行错误处理。通常,对于这种特殊情况,
可能
会倾向于做与您想要的相反的事情(通常您希望继续成功并在第一次失败后停止,这与大多数
可能
机器为这种情况提供的相反).

您正在寻找一种称为
Result
的代数数据类型,它由
data Result=Ok | Error String
之类的内容定义。您可以使用fancier,为不同类型的错误使用类型变量,甚至向
Ok
部分添加内容。Elm内置了这种类型。哈斯克尔呢?可能吧,环顾四周。:)嗯,使用
e()
和创建模式同义词
pattern Success=Right()
怎么样?如果您使用的是一元计算,那么
ExceptT err m()
将是我的第一本能-一个要么成功,要么没有结果的动作(Success)除了“yes it Successed”(是的,它成功了)之外,不返回任何信息的计算在IO操作之外听上去不是很有用,而且据我所知,在该上下文中表示错误的最惯用方法是有例外,不是吗?老实说,这实际上取决于函数的功能。
System.Exit
定义了
data ExitCode=ExitSuccess | ExitFailure Int
,它基本上是Ray Toal的
结果
建议的退出状态特定版本
ExitCode
还有一个
异常
实例,这意味着要做的“标准”事情是定义您自己的特定于域的类型。模式同义词少了一点,因为只有当您已经知道
e()
背后的语义时才会使用。使用
e Success
data Success=Success
会更好
type FailableWith e = Either e ()

pattern FailedWith :: e -> FailableWith e
pattern FailedWith x = Left x