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.
答案 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 :: Void
与absurd undefined
的结果相同。
现在查看absurd a
的定义,它使用seq
来“确保”实际评估a
,因此您认为应触发undefined
例外。但是,巧妙的seq
实际上并没有这样做。 a `seq` b
仅保证 a
和b
都将在整个表达式返回之前进行评估,但不以何种顺序进行评估。完全由实现的选择决定哪个部分首先被评估。
这意味着GHC
可以先评估a
,然后触发异常;或者首先评估spin a
。如果它首先评估spin a
,那么你得到一个无限循环,因为Void
构造函数是一个newtype
构造函数,而且根据设计,展开它们并不会实际评估任何东西。
因此,通过seq
的语义,两个选项实际上都是合法的Haskell行为。
作为最后一点,exception semantics in GHC被定义为明确的不确定性:如果表达式可以用几种不同的方式给出底部,GHC可以选择其中任何一种。由于区分不同底部的唯一方法是IO
,因此这被认为是可以接受的。