报告Haskell错误的最干净方法


22

我正在学习Haskell,并且遇到了三种不同的方法来处理我编写的函数中的错误:

  1. 我可以简单地编写error "Some error message.",从而引发异常。
  2. 我可以让我的函数return Maybe SomeType,在这里我可能无法返回我想返回的内容。
  3. 我可以使用函数return Either String SomeType,在这里我可以返回错误信息或首先要返回的内容。

我的问题是:我应该使用哪种处理错误的方法,为什么?根据上下文,也许我应该使用不同的方法?

我目前的理解是:

  • 在纯功能代码中处理异常是“困难的”,在Haskell中,人们希望将事物保持尽可能纯的功能。
  • Maybe SomeType如果函数失败或成功(即,失败的方式没有不同),则返回是正确的做法
  • Either String SomeType如果函数可能以各种方式中的任何一种失败,则返回是正确的做法。

Answers:


32

好的,Haskell中错误处理的第一条规则:永远不要使用error

各个方面都很糟糕。它纯粹是作为历史的行为而存在,而Prelude使用它的事实是可怕的。不要使用它。

可以想象的唯一使用时间是,当某件事在内部如此可怕,以至于现实的本质一定有问题,从而使程序的结果变得毫无意义。

现在的问题变成MaybeVS EitherMaybe非常适合类似的东西head,它可能会或可能不会返回值,但是只有一个可能的失败原因。Nothing说的是“它坏了,您已经知道原因了”。有人会说这表示部分功能。

错误处理的最可靠形式是Either+错误ADT。

例如,在我的一个业余编译器中,我有类似

data CompilerError = ParserError ParserError
                   | TCError TCError
                   ...
                   | ImpossibleError String

data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
             | MissingDefinition Name
             | InfiniteType Ty
             ...

type ErrorM m = ExceptT CompilerError m -- from MTL

现在,我定义了一堆错误类型,将它们嵌套以使我拥有一个光荣的顶级错误。这可能是任何编译阶段的错误,也可能是ImpossibleError,它表示编译器错误。

这些错误类型中的每一种都试图将尽可能多的信息保留尽可能长的时间,以进行漂亮的打印或其他分析。更重要的是,通过没有字符串,我可以测试通过类型检查器运行输入错误的程序实际上会产生统一错误!一旦某事物成为String,它就永远消失了,并且其中包含的任何信息对于编译器/测试都是不透明的,因此Either String也不是一件好事。

最后,我将这种类型打包到ExceptTMTL的新monad转换器中。从本质EitherT上讲,这是一个不错的批处理函数,它以纯净,令人愉悦的方式提供了引发和捕获错误的功能。

最后,值得一提的是,Haskell具有像其他语言一样支持处理异常的机制,除了捕获异常位于IO。我知道有些人喜欢将它们用于IO繁重的应用程序,这些应用程序可能会导致所有故障,但是这种情况很少见,因此他们不愿意考虑。是否使用这些不纯净的异常还是仅取决于ExceptT Error IO口味。我个人ExceptT之所以选择,是因为我喜欢被提醒失败的机会。


总结一下,

  • Maybe -我可能会以一种明显的方式失败
  • Either CustomType -我会失败的,我会告诉你发生了什么事
  • IO+例外-我有时会失败。检查我的文档以查看我在执行操作时所抛出的内容
  • error -我也讨厌你

这个答案对我来说很清楚。我是基于内置函数喜欢headlast似乎在使用的事实来猜测自己的error,所以我想知道这是否真的是做事的好方法,而我只是想念一些东西。这回答了这个问题。:)
CmdrMoozy 2014年

5
@CmdrMoozy很高兴为您提供帮助:)不幸的是,前奏有如此糟糕的作法。这就是传承的方式:/
Daniel Gratzer 2014年

3
+1您的摘要令人惊叹
recursion.ninja 2014年

2

我不会根据失败的方式将2和3分开。一旦您认为只有一种可能的方法,另一种方法就会露面。度量标准应该改为“我的呼叫者是否在意某件事失败的原因?他们实际上可以采取任何措施吗?”。

除此之外,我Either String SomeType还不清楚会产生错误情况的原因。我将使用具有更描述性名称的条件来制作简单的代数数据类型。

使用哪种方法取决于所遇到问题的性质以及所使用软件包的惯用法。虽然我倾向于避免排名第一。


实际上,使用Either错误是Haskell中非常著名的模式。
Rufflewind 2014年

1
@rufflewind- Either不是不清楚的部分,将其String用作错误而不是实际的字符串。
Telastyn

啊,我误读了你的意图。
Rufflewind
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.