在Haskell中采用简单的身份功能,
id :: forall a. a -> a
鉴于Haskell据称支持不可预测的多态性,我应该能够通过类型归属将id
“限制”到类型(forall a. a -> a) -> (forall b. b -> b)
似乎是合理的。但这不起作用:
Prelude> id :: (forall a. a -> a) -> (forall b. b -> b)
<interactive>:1:1:
Couldn't match expected type `b -> b'
with actual type `forall a. a -> a'
Expected type: (forall a. a -> a) -> b -> b
Actual type: (forall a. a -> a) -> forall a. a -> a
In the expression: id :: (forall a. a -> a) -> (forall b. b -> b)
In an equation for `it':
it = id :: (forall a. a -> a) -> (forall b. b -> b)
当然可以使用所需的签名定义一个新的,受限制的身份函数形式:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId x = x
然而,根据一般id
定义它不起作用:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId = id -- Similar error to above
那么这里发生了什么?看起来这可能与难以理解的困难有关,但启用-XImpredicativeTypes
没有任何区别。
答案 0 :(得分:9)
为什么期望某种
(forall a. a -> a) -> b -> b
我认为类型forall b.(forall a. a -> a) -> b -> b
等同于您提供的类型。它只是它的规范表示,其中forall尽可能向左移动。
它不起作用的原因是给定类型实际上比id :: forall c的类型更多态。 c - &gt; c,这要求参数和返回类型相等。但是你的类型中的forall有效地禁止与任何其他类型统一。
答案 1 :(得分:2)
forall b. (forall a. a -> a) -> b -> b
与(forall a. a -> a) -> (forall b. b -> b)
不等。
除非另有注释,否则类型变量在最外层进行量化。因此(a -> a) -> b -> b
是(forall a. (forall b. (a -> a) -> b -> b))
的简写。在系统F中,类型抽象和应用程序是明确的,这描述了像f = Λa. Λb. λx:(a -> a). λy:b. x y
这样的术语。对于不熟悉符号的人来说,Λ
是一个将类型作为参数的lambda,与λ
不同,它将一个术语作为参数。
f
的来电者首先提供类型参数a
,然后提供类型参数b
,然后提供两个值x
和y
的值选择的类型。需要注意的重要事项是来电者选择a
和b
。因此,调用者可以执行类似f String Int length
的应用程序,以生成术语String -> Int
。
使用-XRankNTypes
您可以通过显式放置通用量词来注释术语,它不必位于最外层。类型为restrictedId
的{{1}}字词可以在系统F中大致举例为(forall a. a -> a) -> (forall b. b -> b)
。请注意g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
(被调用者)如何通过首先使用类型实例化g
和x
{/ 1}}。
但在这种情况下,调用者不能像以前那样使用0
选择类型参数。您将在lambda中注意应用程序'e'
和f
。这会强制调用者提供多态函数,因此像x Int
这样的术语无效,因为x Char
不适用于g length
或length
。
考虑它的另一种方法是将Int
和Char
的类型绘制为树。 f
的树具有通用量词作为根,而g
的树具有箭头作为根。要到达f
中的箭头,调用者将实例化两个量词。对于g
,它已经是箭头类型,并且调用者无法控制实例化。这会强制调用者提供多态参数。
最后,请原谅我做出的例子。 Gabriel Scherer描述了Moderately Practical uses of System F over ML中更高级别多态性的一些更实际的用法。您还可以参考TAPL的第23和30章,或者浏览编译器扩展的文档,以找到更高级别的多态性的更多细节或更好的实际示例。
答案 2 :(得分:-2)
我不是关于预测类型的专家,所以这既是一个潜在的答案,也是尝试从评论中学习的东西。
专门化
是没有意义的\/ a . a -> a (1)
到
(\/ a . a -> a) -> (\/ b . b -> b) (2)
我不认为不可预测的类型是允许它的理由。量词通常具有使(2)不等价集的左侧和右侧表示的类型的效果。然而,(1)中的a -> a
意味着左侧和右侧是等价的集合。
E.g。你可以将(2)具体化为(int - &gt; int) - &gt; (string - &gt; string)。但是通过任何系统,我知道这不是由(1)表示的类型。
错误消息看起来像是由Haskel类型推理器尝试统一id
的类型
\/ a . a -> a
你给的类型
\/ c . (c -> c) -> \/ d . (d -> d)
为了清楚起见,我在这里将量化变量统一起来。
类型推断器的工作是找到a
,c
和d
的最常规分配,使两个表达式在语法上相等。它最终发现需要统一c
和d
。由于它们是单独量化的,所以它处于死路并退出。
您可能会问这个问题,因为基本类型推理器 - 带有归属(c -> c) -> (d -> d)
- 只会向前推进并设置c == d
。结果类型为
(c -> c) -> (c -> c)
这只是
的简写\/c . (c -> c) -> (c -> c)
这可证明是x = x
类型中最不常见的类型(类型理论最小上限)表达式,其中x
被约束为具有相同域和共域的函数。
给定的“restrictededId”的类型在实际意义上过于笼统。虽然它永远不会导致运行时类型错误,但是你给它的表达式描述了许多类型 - 就像前面提到的(int -> int) -> (string -> string)
- 即使你的类型允许它们,这在操作上是不可能的。