好的,Haskell中错误处理的第一条规则:永远不要使用error
。
各个方面都很糟糕。它纯粹是作为历史的行为而存在,而Prelude使用它的事实是可怕的。不要使用它。
可以想象的唯一使用时间是,当某件事在内部如此可怕,以至于现实的本质一定有问题,从而使程序的结果变得毫无意义。
现在的问题变成Maybe
VS Either
。Maybe
非常适合类似的东西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
也不是一件好事。
最后,我将这种类型打包到ExceptT
MTL的新monad转换器中。从本质EitherT
上讲,这是一个不错的批处理函数,它以纯净,令人愉悦的方式提供了引发和捕获错误的功能。
最后,值得一提的是,Haskell具有像其他语言一样支持处理异常的机制,除了捕获异常位于IO
。我知道有些人喜欢将它们用于IO
繁重的应用程序,这些应用程序可能会导致所有故障,但是这种情况很少见,因此他们不愿意考虑。是否使用这些不纯净的异常还是仅取决于ExceptT Error IO
口味。我个人ExceptT
之所以选择,是因为我喜欢被提醒失败的机会。
总结一下,
Maybe
-我可能会以一种明显的方式失败
Either CustomType
-我会失败的,我会告诉你发生了什么事
IO
+例外-我有时会失败。检查我的文档以查看我在执行操作时所抛出的内容
error
-我也讨厌你
head
或last
似乎在使用的事实来猜测自己的error
,所以我想知道这是否真的是做事的好方法,而我只是想念一些东西。这回答了这个问题。:)