[英]Why can't the type of id be specialised to (forall a. a -> a) -> (forall b. b -> b)?
Take the humble identity function in Haskell, 在Haskell中采用简单的身份功能,
id :: forall a. a -> a
Given that Haskell supposedly supports impredicative polymorphism, it seems reasonable that I should be able to "restrict" id
to the type (forall a. a -> a) -> (forall b. b -> b)
via type ascription. 鉴于Haskell据说支持不可预测的多态性,我应该能够通过类型归属将id
“限制”到类型(forall a. a -> a) -> (forall b. b -> b)
a。a (forall a. a -> a) -> (forall b. b -> b)
似乎是合理的。 But this doesn't work: 但这不起作用:
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)
It's of course possible to define a new, restricted form of the identity function with the desired signature: 当然可以使用所需的签名定义一个新的,受限制的身份函数形式:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId x = x
However defining it in terms of the general id
doesn't work: 但是,根据通用id
定义它不起作用:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId = id -- Similar error to above
So what's going on here? 那么这里发生了什么? It seems like it might be related to difficulties with impredicativity, but enabling -XImpredicativeTypes
makes no difference. 看起来它可能与难以理解的困难有关,但启用-XImpredicativeTypes
没有任何区别。
why is it expecting a type of
(forall a. a -> a) -> b -> b
为什么它期待一种类型(forall a. a -> a) -> b -> b
I think the type forall b.(forall a. a -> a) -> b -> b
is equivalent to the type you gave. 我认为类型forall b.(forall a. a -> a) -> b -> b
等同于你给出的类型。 It is just a canonical representation of it, where the forall is shifted as much to the left as possible. 它只是它的规范表示,其中forall尽可能向左移动。
And the reason why it does not work is that the given type is actually more polymorphic than the type of id :: forall c. 并且它不起作用的原因是给定类型实际上比id :: forall c的类型更多态。 c -> c, which requires that argument and return types be equal. c - > c,它要求参数和返回类型相等。 But the forall a in your type effectively forbids a to be unified with any other type. 但是你的类型中的forall有效地禁止与任何其他类型统一。
You are absolutely correct that forall b. (forall a. a -> a) -> b -> b
你是绝对正确的forall b. (forall a. a -> a) -> b -> b
forall b. (forall a. a -> a) -> b -> b
is not equivalent to (forall a. a -> a) -> (forall b. b -> b)
. forall b. (forall a. a -> a) -> b -> b
不等同于(forall a. a -> a) -> (forall b. b -> b)
。
Unless annotated otherwise, type variables are quantified at the outermost level. 除非另有注释,否则类型变量在最外层进行量化。 So (a -> a) -> b -> b
is shorthand for (forall a. (forall b. (a -> a) -> b -> b))
. 所以(a -> a) -> b -> b
是(forall a. (forall b. (a -> a) -> b -> b))
简写。 In System F, where type abstraction and application are made explicit, this describes a term like f = Λa. Λb. λx:(a -> a). λy:b. xy
在系统F中,类型抽象和应用程序是明确的,这描述了像f = Λa. Λb. λx:(a -> a). λy:b. xy
这样的术语f = Λa. Λb. λx:(a -> a). λy:b. xy
f = Λa. Λb. λx:(a -> a). λy:b. xy
f = Λa. Λb. λx:(a -> a). λy:b. xy
. f = Λa. Λb. λx:(a -> a). λy:b. xy
。 Just to be clear for anyone not familiar with the notation, Λ
is a lambda that takes a type as a parameter, unlike λ
which takes a term as a parameter. 对于不熟悉符号的人来说, Λ
是一个lambda,它将一个类型作为参数,与λ
不同,它以一个术语作为参数。
The caller of f
first provides a type parameter a
, then supplies a type parameter b
, then supplies two values x
and y
that adhere to the chosen types. f
的调用者首先提供类型参数a
,然后提供类型参数b
,然后提供符合所选类型的两个值x
和y
。 The important thing to note is the caller chooses a
and b
. 需要注意的重要事项是呼叫者选择a
和b
。 So the caller can perform an application like f String Int length
for example to produce a term String -> Int
. 因此调用者可以执行像f String Int length
这样的应用程序,例如生成一个术语String -> Int
。
Using -XRankNTypes
you can annotate a term by explicitly placing the universal quantifier, it doesn't have to be at the outermost level. 使用-XRankNTypes
可以通过显式放置通用量词来注释术语,它不必位于最外层。 Your restrictedId
term with the type (forall a. a -> a) -> (forall b. b -> b)
could be roughly exemplified in System F as g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
您的restrictedId
术语类型(forall a. a -> a) -> (forall b. b -> b)
可以在系统F中大致举例说明为g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
. g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
。 Notice how g
, the callee, can apply x
to both 0
and 'e'
by instantiating it with a type first. 注意g
被调用者如何通过首先用类型实例化x
来将x
应用于0
和'e'
。
But in this case the caller cannot choose the type parameter like it did before with f
. 但在这种情况下,调用者不能像之前用f
那样选择类型参数。 You'll note the applications x Int
and x Char
inside the lambda. 你会注意到lambda中的应用程序x Int
和x Char
。 This forces the caller to provide a polymorphic function, so a term like g length
is not valid because length
does not apply to Int
or Char
. 这会强制调用者提供多态函数,因此像g length
这样的术语无效,因为length
不适用于Int
或Char
。
Another way to think about it is drawing the types of f
and g
as a tree. 考虑它的另一种方法是将f
和g
的类型绘制为树。 The tree for f
has a universal quantifier as the root while the tree for g
has an arrow as the root. f
的树具有通用量词作为根,而g
的树具有箭头作为根。 To get to the arrow in f
, the caller instantiates the two quantifiers. 为了获得f
的箭头,调用者实例化两个量词。 With g
, it's already an arrow type and the caller cannot control the instantiation. 使用g
,它已经是箭头类型,调用者无法控制实例化。 This forces the caller to provide a polymorphic argument. 这会强制调用者提供多态参数。
Lastly, please forgive my contrived examples. 最后,请原谅我做作的例子。 Gabriel Scherer describes some more practical uses of higher-rank polymorphism in Moderately Practical uses of System F over ML . Gabriel Scherer描述了更高等级多态性在System F的中等实际使用中对ML的更实际用途。 You might also consult chapters 23 and 30 of TAPL or skim the documentation for the compiler extensions to find more detail or better practical examples of higher-rank polymorphism. 您还可以参考TAPL的第23和30章,或浏览编译器扩展的文档,以查找更高详细信息或更高级别多态性的实际示例。
I'm not an expert on impredictive types, so this is at once a potential answer and a try at learning something from comments. 我不是关于不可预测类型的专家,所以这既是一个潜在的答案,也是尝试从评论中学习一些东西。
It doesn't make sense to specialize 专业化没有意义
\/ a . a -> a (1)
to 至
(\/ a . a -> a) -> (\/ b . b -> b) (2)
and I don't think impredictive types are a reason to allow it. 我不认为不可预测的类型是允许它的理由。 The quantifiers have the effect of making the types represented by the left and right side of (2) inequivalent sets in general. 量词通常具有使(2)不等价集的左侧和右侧表示的类型的效果。 Yet the a -> a
in (1) implies left and right side are equivalent sets. 然而a -> a
in(1)意味着左侧和右侧是等价的集合。
Eg you can concretize (2) to (int -> int) -> (string -> string). 例如,你可以将(2)具体化为(int - > int) - >(string - > string)。 But by any system I know this is not a type represented by (1). 但是通过任何系统,我知道这不是(1)所代表的类型。
The error message looks like it results from an attempt by the Haskel type inferencer to unify the type of id
错误消息看起来像是由Haskel类型推理器尝试统一id
的类型而产生的
\/ a . a -> a
with the type you've given 与你给出的类型
\/ c . (c -> c) -> \/ d . (d -> d)
Here I'm uniqifying quantified variables for clarity. 在这里,为了清晰起见,我将量化变量统一起来。
The job of the type inferencer is to find a most general assignment for a
, c
, and d
that causes the two expressions to be syntactically equal. 类型inferencer的任务是找到一个最普通的任务a
, c
和d
使得两个表达式是语法相等。 It ultimately finds that it's required to unify c
and d
. 它最终发现需要统一c
和d
。 Since they're separately quantified, it's at a dead end and quits. 由于它们是单独量化的,所以它处于死路并退出。
You are perhaps asking the question because the basic type inferencer -- with an ascription (c -> c) -> (d -> d)
-- would just plow ahead and set c == d
. 你可能会问这个问题,因为基本类型推理器 - 带有归属(c -> c) -> (d -> d)
- 只需要向前推进并设置c == d
。 The resulting type would be 结果类型将是
(c -> c) -> (c -> c)
which is just shorthand for 这只是简写
\/c . (c -> c) -> (c -> c)
This is provably the least most general type (type theoretic least upper bound) expression for the type of x = x
where x
is constrained to be a function with the same domain and co-domain. 对于x = x
的类型,这可证明是最不常见的类型(类型理论上最小上界)表达式,其中x
被约束为具有相同域和共域的函数。
The type of "restricedId" as given is in a real sense excessively general. 给出的“restrictededId”的类型在实际意义上过于笼统。 While it can never lead to a runtime type error, there are many types described by the expression you've given it - like the aforementioned (int -> int) -> (string -> string)
- that are impossible operationally even though your type would allow them. 虽然它永远不会导致运行时类型错误,但是你给它的表达式描述了很多类型 - 比如前面提到的(int -> int) -> (string -> string)
- 即使你的操作也是不可能的类型会允许他们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.