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