[在书Thinking with Types中,6.4 Continuation Monad
告诉类型a
和forall r. (a -> r) -> r
是同构的,可以通过以下函数来证明:
cont :: a -> (forall r. (a -> r) -> r)
cont x = \f -> f x
unCont :: (forall r. (a -> r) -> r) -> a
unCont f = f id
在本书中,它告诉我们具有相同基数的任何两种类型将总是彼此同构。因此,我尝试找出类型为a
和forall r. (a -> r) -> r
的基数。
假设类型为a
的基数为|a|
。那么对于forall r. (a -> r) -> r
类型,如何找出其基数等于|a|
?函数类型a -> b
的基数为|b|^|a|
,即|b|
为|a|
的幂,因此forall r. (a -> r) -> r
的基数为|r|^(|r|^|a|)
。怎么等于|a|
?
我很困惑。感谢您的提示!
基数在存在多态类型时不能真正定义。如今,人们已经知道,多态类型并不是最初可能想到的“集合”。雷诺兹在其论文《多态不是集合论》中提出了一个著名的开创性论据,证明了我们不能简单地以“琐碎”的方式解释集合的类型并获得有意义的概念。
实际上,在集合2^K
和K
中是不同的基数,第一个更大。同样,2^(2^K)
大于K
。然而,F X = 2^(2^X)
(类似于F a = (a -> Bool) -> Bool
)形成一个(协变)函子,为此我们可以找到一个固定点
newtype T = T ((T -> Bool) -> Bool)
获得T
与2^(2^T)
同构,这在集合中没有意义,正是因为它们不能具有相同的基数。
((在存在多态性的情况下,即使没有递归类型,也可以通过编码为T
来获得上述类型forall a. (F a -> a) -> a
。]
无论如何,为了解决这一僵局,我们需要将a -> Bool
解释为功能集2^a
以外的其他东西。一种可能的解决方案是使用Scott-continuous functions,如Scott所做的那样。一个相关的解决方案是使用stable functions(请参阅Girard的“证明和类型”一书),该书(如果我没记错的话)使T
和T -> Bool
的解释具有same基数(除非两者都是有限的。
因此,在存在多态类型时,基数不是用于检查类型同构的正确工具。我们确实需要查看是否有可能制作同构函数及其逆函数,就像您在问题中发布的一样。
基数参数实际上不适用于多态类型(请参阅@chi的答案)。
但是同构本身可以这样直观地解释:
类型forall r. (a -> r) -> r
的意思是<< [如果您给我一个将a
转换为r
的函数,我可以给您返回r
。哦,我可以对任何可能的r
执行此操作无论如何“
a
。 由于我承诺将对所有可能的r
执行此操作,这意味着我对r
本身一无所知,包括如何构造其值。我唯一可以使用的就是您给我的功能a -> r
。调用此函数的唯一方法是给它一个a
。
这意味着,如果我做出这样的承诺,我必须已经秘密地背着一个a
。
并且在您的问题中,您已经显示了两种转换:cont
转换一种方式,unCont
转换另一种方式。您可以简单地显示cont . unCont = unCont . cont = id
。因此,类型是同构的。
虽然显示这两种转换的存在较为正式,但我发现对这两种类型确实是真正的“同一个东西”的直觉并不总是令人满意,因此我在上面给出了直观的解释。