Haskell Data.Void: undefined 变成死循环
Haskell Data.Void: undefined turns into infinite loop
我注意到 haskell Data.Void
模块中 Void
类型的一件事,这很奇怪,我非常想知道为什么会这样.
undefined 应该是每个类型都存在的值(在我的理解中),并且
undefined :: Type
应该产生
*** Exception: Prelude.undefined
它适用于我尝试过的所有数据类型,Void
除外。
undefined :: Void
根本不会终止,absurd undefined
也是如此。
现在我认为这不是什么大问题,因为 Void
代表一个无论如何都不会发生的值,但我仍然想知道是什么导致了这种行为。
经过更多的调查,我想我明白这是怎么发生的,以及它如何取决于 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
到 "ensure" 实际评估 a
,因此您会认为应该触发 undefined
异常。然而,微妙的 seq
实际上并不是那样工作的。 a `seq` b
仅保证 both a
和 b
将在整个表达式 returns 之前求值,但 不会按哪个顺序。这完全取决于实现的选择,首先评估哪一部分。
这意味着 GHC
可以自由地先评估 a
,触发异常;否则首先评估 spin a
。如果它首先计算 spin a
,那么你会得到一个无限循环,因为 Void
构造函数是一个 newtype
构造函数,并且根据设计,解包它们实际上不会计算任何东西。
因此,根据 seq
的语义,这两个选项实际上都是同样合法的 Haskell 行为。
作为最后的说明,exception semantics in GHC 已被明确定义为非确定性的:如果一个表达式可以以几种不同的方式给出 bottom,GHC 可以选择其中的任何一种。由于区分不同底部的唯一方法是IO
,这被认为是可以接受的。
我注意到 haskell Data.Void
模块中 Void
类型的一件事,这很奇怪,我非常想知道为什么会这样.
undefined 应该是每个类型都存在的值(在我的理解中),并且
undefined :: Type
应该产生
*** Exception: Prelude.undefined
它适用于我尝试过的所有数据类型,Void
除外。
undefined :: Void
根本不会终止,absurd undefined
也是如此。
现在我认为这不是什么大问题,因为 Void
代表一个无论如何都不会发生的值,但我仍然想知道是什么导致了这种行为。
经过更多的调查,我想我明白这是怎么发生的,以及它如何取决于 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
到 "ensure" 实际评估 a
,因此您会认为应该触发 undefined
异常。然而,微妙的 seq
实际上并不是那样工作的。 a `seq` b
仅保证 both a
和 b
将在整个表达式 returns 之前求值,但 不会按哪个顺序。这完全取决于实现的选择,首先评估哪一部分。
这意味着 GHC
可以自由地先评估 a
,触发异常;否则首先评估 spin a
。如果它首先计算 spin a
,那么你会得到一个无限循环,因为 Void
构造函数是一个 newtype
构造函数,并且根据设计,解包它们实际上不会计算任何东西。
因此,根据 seq
的语义,这两个选项实际上都是同样合法的 Haskell 行为。
作为最后的说明,exception semantics in GHC 已被明确定义为非确定性的:如果一个表达式可以以几种不同的方式给出 bottom,GHC 可以选择其中的任何一种。由于区分不同底部的唯一方法是IO
,这被认为是可以接受的。