为什么未定义的函数在与未装箱的类型一起使用时是轻度多态的?

Why is the undefined function levity-polymorphic when using it with unboxed types?

我刚读完论文 Levity Polymorphism

我有一个问题,为什么 undefined 在用作未装箱类型时可以是轻度多态的。

首先,让我们从论文中对boxity的一些定义开始:

论文后面有一些轻浮的定义:

本文继续解释 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 个值:TrueFalse。表达式 ⊥ 根本不是一个值。它是一个表达式,一个变量可以绑定到 ⊥,但这不会使 ⊥ 成为一个值。也许更好的说法是,如果 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 斗争了很长一段时间,并使用了各种恶作剧来解决它。然后,当我们意识到 undefinedHasCallStack 约束时,我们发现我们可以完全避开这个问题。老实说,如果没有 HasCallStack.

看似无关紧要的用户便利,我不知道我们会做什么