Haskell中的异常是如何工作的?

89

在 GHCi 中:

Prelude> error (error "")
*** Exception: 
Prelude> (error . error) ""
*** Exception: *** Exception: 

为什么第一个异常不是嵌套异常?


9
这是 GHC 可以进行的一种转换:“我是一个独立运行的编译器,所有 | 都对我来说是相同的”。你是在询问使这两行代码编译不同的实现细节吗? - shachaf
3
error 是一种特殊的错误机制,不是真正的异常机制。如果想要使用真正可以捕获的异常,请看 Error monad。 - Cat Plus Plus
5
另外请迭代错误“”!! n和令人惊叹的“修复错误”。 - Vitus
9
我总是假装 error = error,并据此编写程序。 - Gabriella Gonzalez
1
@GabrielGonzalez 这实际上是在 Gofer prelude 中编写 undefined 的方式:http://hackage.haskell.org/packages/archive/gofer-prelude/2.30.3/doc/html/src/Prelude-Gofer.html#undefined - Don Stewart
显示剩余2条评论
1个回答

104
这是关于“不精确异常”的语义(有些令人惊讶)。当纯代码可以被证明会产生一组异常值时(即errorundefined的值,并且明确地不是在IO中生成的那种异常),则语言允许返回该集合中的任何一个值。在Haskell中,异常值更像是浮点代码中的NaN,而不是命令式语言中基于控制流的异常。即使对于高级的Haskellers来说,偶尔也会遇到这样的情况。
 case x of
   1 -> error "One"
   _ -> error "Not one"

由于代码的评估结果是一组异常,GHC可以随意选择其中一个。如果开启优化,你可能会发现它总是评估为“Not one”。

为什么要这样做呢?因为否则我们会过度限制语言的评估顺序,例如我们将不得不为以下内容固定确定性结果:

 f (error "a") (error "b")

例如,可以要求按照从左到右的顺序计算,以防出现错误值。 这非常不像 Haskell!

由于我们不想削弱代码的优化以支持 error,因此解决方案是指定结果是异常值集合中的一个非确定性选择:不精确的异常! 在某种程度上,所有异常都被返回,并且选择其中一个。

通常,您不关心 - 异常就是异常 - 除非您关心异常中的字符串,在这种情况下,使用 error 进行调试会非常令人困惑。


参考文献:A semantics for imprecise exceptions,Simon Peyton Jones,Alastair Reid,Tony Hoare,Simon Marlow,Fergus Henderson。 Proc Programming Languages Design and Implementation(PLDI'99),Atlanta。(PDF


2
我理解 GHC 会选择可能遇到的任何异常之一。但在你的“case”示例中,“Not one”异常在输入为1时无法遇到,所以我仍然将其归类为错误。 - Peaker
8
死代码消除 - 优化器无需查看x即可知道错误是结果,所有分支产生的值都是“相同”的,因此可以完全忽略输入值。在不精确的异常情况下不算错误。 - Don Stewart
1
@lpsmith:我认为所有异常类型都是不精确的(当使用throw抛出时),而您可以使用throwIO确定性地抛出异常。 - FunctorSalad
4
@Peaker,我认为你是对的。如果GHC想要按照《不精确异常论文》中规定的规则进行操作,那么我认为他们不应该优化这个表达式。 - augustss
3
这篇不太准确的例外处理论文似乎在说,如果“案例选择器”是一个错误值,那么分支中的错误值就可以被返回。所以 case error "banana" of (x:xs) -> error "bonobo" 可以给你 * Exception: bonobo - Ben Millwood
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接