为什么未定义的函数在与未装箱的类型一起使用时是轻度多态的?
Why is the undefined function levity-polymorphic when using it with unboxed types?
我刚读完论文 Levity Polymorphism。
我有一个问题,为什么 undefined
在用作未装箱类型时可以是轻度多态的。
首先,让我们从论文中对boxity的一些定义开始:
盒装:
A boxed 值由指向堆的指针表示。
Int
和 Bool
是具有 boxed 值的类型示例。
未装箱:
论文后面有一些轻浮的定义:
解除:
A lifted类型是懒惰的。
一个lifted类型在正常元素之外有一个额外的元素,代表一个非终止计算。
例如,类型Bool
是lifted,意思是Bool
有三个不同的值:True
、False
和 ⊥
(底部)。
所有 lifted 类型必须 boxed.
未解除
unlifted 类型是严格的。
元素 ⊥
不存在于 unlifted 类型中。
Int#
和 Char#
是 unlifted 类型的示例。
本文继续解释 GHC 8 如何提供允许类型变量具有多态性的功能。
这允许您编写具有以下类型的轻度多态 undefined
:
undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r). a
这表示 undefined
应该适用于 任何 RuntimeRep
,甚至是未提升的类型。
这是在 GHCi 中使用 undefined
作为未装箱、未提升的 Int#
的示例:
> :set -XMagicHash
> import GHC.Prim
> import GHC.Exts
> I# (undefined :: Int#)
*** Exception: Prelude.undefined
我一直认为 undefined
与 ⊥
(底部)相同。然而,论文说,"The element ⊥
does not exist in an unlifted type."
这是怎么回事? undefined
用作未提升类型时实际上不是 ⊥
吗?我是不是对这篇论文有什么误解(或方方正正或轻率)?
此处 "Levity Polymorphism" 论文的作者。
首先,我想澄清一下上面的几个误区:
Bool
确实是举升、盒装型。但是,它仍然只有 2 个值:True
和 False
。表达式 ⊥ 根本不是一个值。它是一个表达式,一个变量可以绑定到 ⊥,但这不会使 ⊥ 成为一个值。也许更好的说法是,如果 x :: Bool
,那么计算 x
会导致三种不同的结果:计算为 True
、计算为 False
和 ⊥ .在这里,⊥ 代表任何不会正常终止的计算,包括抛出异常(这是 undefined
真正做的)和永远循环。
与最后一点类似,undefined
不是一个值。它是任何类型的居民,但它不是一个值。值是在评估时什么都不做的东西——但 undefined
不符合该规范。
取决于你如何看待它,⊥可以存在于未提升的类型中。例如,采用这个定义:
loop :: () -> Int#
loop x = loop x
该定义被 GHC 接受。然而 loop ()
是未提升、未装箱类型 Int#
的 ⊥ 元素。 unlifted type 和 lifted type 在这方面的区别是无法将变量绑定到 loop ()
。任何这样做的尝试(比如 let z = loop () in ...
)都会在变量被绑定之前循环。
请注意,这与非托管语言(如 C)中的无限递归函数没有什么不同。
那么,我们如何允许 undefined
具有未提升的类型? @dfeuer 说得对:undefined
是一个秘密函数。它的完整类型签名是 undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
,这意味着它是一个函数,就像上面的 loop
一样。在运行时,它将当前调用堆栈作为参数。因此,无论何时使用 undefined
,您都只是在调用一个抛出异常的函数,不会造成任何麻烦。
确实,在设计 levity 多态性时,我们与 undefined
斗争了很长一段时间,并使用了各种恶作剧来解决它。然后,当我们意识到 undefined
有 HasCallStack
约束时,我们发现我们可以完全避开这个问题。老实说,如果没有 HasCallStack
.
看似无关紧要的用户便利,我不知道我们会做什么
我刚读完论文 Levity Polymorphism。
我有一个问题,为什么 undefined
在用作未装箱类型时可以是轻度多态的。
首先,让我们从论文中对boxity的一些定义开始:
盒装:
A boxed 值由指向堆的指针表示。
Int
和Bool
是具有 boxed 值的类型示例。
未装箱:
论文后面有一些轻浮的定义:
解除:
A lifted类型是懒惰的。
一个lifted类型在正常元素之外有一个额外的元素,代表一个非终止计算。
例如,类型
Bool
是lifted,意思是Bool
有三个不同的值:True
、False
和⊥
(底部)。所有 lifted 类型必须 boxed.
未解除
unlifted 类型是严格的。
元素
⊥
不存在于 unlifted 类型中。Int#
和Char#
是 unlifted 类型的示例。
本文继续解释 GHC 8 如何提供允许类型变量具有多态性的功能。
这允许您编写具有以下类型的轻度多态 undefined
:
undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r). a
这表示 undefined
应该适用于 任何 RuntimeRep
,甚至是未提升的类型。
这是在 GHCi 中使用 undefined
作为未装箱、未提升的 Int#
的示例:
> :set -XMagicHash
> import GHC.Prim
> import GHC.Exts
> I# (undefined :: Int#)
*** Exception: Prelude.undefined
我一直认为 undefined
与 ⊥
(底部)相同。然而,论文说,"The element ⊥
does not exist in an unlifted type."
这是怎么回事? undefined
用作未提升类型时实际上不是 ⊥
吗?我是不是对这篇论文有什么误解(或方方正正或轻率)?
此处 "Levity Polymorphism" 论文的作者。
首先,我想澄清一下上面的几个误区:
Bool
确实是举升、盒装型。但是,它仍然只有 2 个值:True
和False
。表达式 ⊥ 根本不是一个值。它是一个表达式,一个变量可以绑定到 ⊥,但这不会使 ⊥ 成为一个值。也许更好的说法是,如果x :: Bool
,那么计算x
会导致三种不同的结果:计算为True
、计算为False
和 ⊥ .在这里,⊥ 代表任何不会正常终止的计算,包括抛出异常(这是undefined
真正做的)和永远循环。与最后一点类似,
undefined
不是一个值。它是任何类型的居民,但它不是一个值。值是在评估时什么都不做的东西——但undefined
不符合该规范。取决于你如何看待它,⊥可以存在于未提升的类型中。例如,采用这个定义:
loop :: () -> Int# loop x = loop x
该定义被 GHC 接受。然而
loop ()
是未提升、未装箱类型Int#
的 ⊥ 元素。 unlifted type 和 lifted type 在这方面的区别是无法将变量绑定到loop ()
。任何这样做的尝试(比如let z = loop () in ...
)都会在变量被绑定之前循环。请注意,这与非托管语言(如 C)中的无限递归函数没有什么不同。
那么,我们如何允许 undefined
具有未提升的类型? @dfeuer 说得对:undefined
是一个秘密函数。它的完整类型签名是 undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
,这意味着它是一个函数,就像上面的 loop
一样。在运行时,它将当前调用堆栈作为参数。因此,无论何时使用 undefined
,您都只是在调用一个抛出异常的函数,不会造成任何麻烦。
确实,在设计 levity 多态性时,我们与 undefined
斗争了很长一段时间,并使用了各种恶作剧来解决它。然后,当我们意识到 undefined
有 HasCallStack
约束时,我们发现我们可以完全避开这个问题。老实说,如果没有 HasCallStack
.