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
的类型签名。签名如何有效?编译器是否识别无限递归函数并允许它们具有任何返回类型,知道如果调用它们将永远不会实际终止?不仅absurd = unsafeCoerce
具有相同的效果吗?
答案 0 :(得分:5)
不,编译器无法识别无限循环函数。实际上已知这是不可能的 - 见Halting Problem。
签名有效,因为函数体中没有任何内容可以确定(或有什么影响)返回类型应该是什么。因此,返回类型可以是任何东西。就这么简单。
Void
的要点是,它是一种根本不具备任何价值的类型。就像一个空集。在这种情况下,通过使Void
的每个值包含另一个值Void
的聪明技巧来实现该属性,从而使得无法构造Void
的值。这意味着,出于所有实际目的,类型Void
不能具有任何值。
相应的函数absurd
是一个永远不能被调用的函数。该属性遵循以Void
为参数的函数。由于不存在类型Void
的值,因此无法为此类函数提供参数,因此无法调用它。这种功能在一些非常高级的边缘情况下很有用,但大多数情况下它是理论上的好奇心。
答案 1 :(得分:4)
absurd
的实现并不重要,因为不可能有Void
的值 - 没有办法构建它。这也是为什么拥有这样一个功能是安全的 - 它不是无中生有的东西,它永远不会发生,所以它只用于类型检查器的证据。所以,unsafeCoerce
也可以安全使用。
但是,是的,无限递归函数可以被输入为具有任何返回类型并且将进行类型检查 - 它不是在类型检查器中编码的特定特征,它只是从我们拥有的其他规则中脱离出来。查看它的一种方法是,由于函数是无限递归的,因此没有证据表明你将返回类型表示为无效。