空虚和荒谬是如何工作的

How do Void and absurd work

Void 的源代码状态:

newtype Void = Void Void

instance showVoid :: Show Void where
    show = absurd

absurd :: forall a. Void -> a
absurd a = spin a
    where
    spin (Void b) = spin b

好像Void是无限递归类型,而absurd是无限递归函数。我在 REPL 中尝试了 运行 show ((unsafeCoerce "lol") :: Void),它立即进入了无限循环。

这里困扰我的是 absurd :: forall a. Void -> a 的类型签名。签名如何有效?编译器是否识别无限递归函数并允许它们具有任何 return 类型,知道它们在调用时永远不会实际终止? absurd = unsafeCoerce 不是一样的效果吗?

不,编译器不识别无限循环函数。这实际上是不可能的 - 请参阅 Halting Problem.

签名有效,因为函数体中没有任何内容可以确定(或影响)return 类型应该是什么。因此,return 类型可以是任何类型。就这么简单。


Void 的要点是它是一种根本不能有任何值的类型。就像一个空集。在这种情况下,属性 是通过使 Void 的每个值都包含另一个 Void 的巧妙技巧来实现的,因此不可能构造 Void 的值.这意味着,出于所有实际目的,类型 Void 不能有任何值。

对应的函数absurd是永远无法调用的函数。此 属性 源自以 Void 作为参数的函数。由于不能有 Void 类型的值,因此无法为该函数提供参数,因此无法调用它。这种功能在一些非常高级的边缘情况下很有用,但主要是理论上的好奇心。

absurd 的实现并不重要,因为不可能有 Void 的值 - 无法构造它。这也是为什么拥有这样一个函数是安全的——它不是无中生有,它永远不会发生,所以它只用于类型检查器的证据。所以是的,unsafeCoerce 也可以安全使用。

但是,是的,无限递归函数可以输入为任何 return 类型,这将进行类型检查 - 它不是类型检查器中编码的特定功能,它只是从其他规则中脱离出来我们有。一种看待它的方法是,由于该函数是无限递归的,因此没有证据表明您将 return 类型描述为存在的任何内容。