Haskell Data.Void: undefined turns into infinite loop

时间:2015-07-29 00:25:09

标签: haskell void

I noticed one thing about the x=[1 1.9 2.8 4.1] type in the haskell Void module, which is weird and I'd very much like to know why it is the way it is. Undefined is supposed to be a value that exists for every type (in my understanding), and

Data.Void

is supposed to yield

undefined :: Type

which works for every data type I have tried, except for *** Exception: Prelude.undefined . Void does not terminate at all, same for undefined :: Void. Now I don't think that's really much of an issue since absurd undefined represents a value that is not going to happen anyway, but I would still like to know what causes this behavior.

1 个答案:

答案 0 :(得分:8)

经过一番调查后,我想我明白了这是怎么发生的,以及它如何依赖于GHC的优化选择。在void-0.7中,我们有以下相关定义:

newtype Void = Void Void

absurd :: Void -> a
absurd a = a `seq` spin a where
   spin (Void b) = spin b

instance Show Void where
  showsPrec _ = absurd

请注意,Show实例委托给absurd,这解释了为什么GHCi中的undefined :: Voidabsurd undefined的结果相同。

现在查看absurd a的定义,它使用seq来“确保”实际评估a,因此您认为应触发undefined例外。但是,巧妙的seq实际上并没有这样做。 a `seq` b仅保证 ab都将在整个表达式返回之前进行评估,但以何种顺序进行评估。完全由实现的选择决定哪个部分首先被评估。

这意味着GHC可以先评估a,然后触发异常;或者首先评估spin a。如果它首先评估spin a,那么你得到一个无限循环,因为Void构造函数是一个newtype构造函数,而且根据设计,展开它们并不会实际评估任何东西。

因此,通过seq的语义,两个选项实际上都是合法的Haskell行为。

作为最后一点,exception semantics in GHC被定义为明确的不确定性:如果表达式可以用几种不同的方式给出底部,GHC可以选择其中任何一种。由于区分不同底部的唯一方法是IO,因此这被认为是可以接受的。