简体   繁体   English

为什么id的类型不能专门用于(forall a.a - > a) - >(forall b.b - > b)?

[英]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 ,然后提供符合所选类型的两个值xy The important thing to note is the caller chooses a and b . 需要注意的重要事项是呼叫者选择ab 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 Intx 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不适用于IntChar

Another way to think about it is drawing the types of f and g as a tree. 考虑它的另一种方法是将fg的类型绘制为树。 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的任务是找到一个最普通的任务acd使得两个表达式是语法相等。 It ultimately finds that it's required to unify c and d . 它最终发现需要统一cd 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.

相关问题 为什么是forall a。 一个不被认为是Int的子类型,而我可以使用类型forall a的表达式。 预计会有任何一种类型的Int? - Why is forall a. a not considered a subtype of Int while I can use an expression of type forall a. a anywhere one of type Int is expected? 为什么`forall(a :: j)(b :: k)`与`forall(p ::(j,k))`的工作方式不同? - Why does `forall (a :: j) (b:: k)` work differently than `forall (p :: (j,k))`? 为什么是 `[1, &quot;a&quot;] :: [forall a. 不允许显示 a =&gt; a]`? - Why is `[1, "a"] :: [forall a. Show a => a]` not allowed? 我可以证明(forall x。Coercible(ax)(bx))隐含ab吗? - Can I prove that (forall x. Coercible (a x) (b x)) implies Coercible a b? forall a之间有什么区别? [a]和[forall a。一个]? - What is the difference between forall a. [a] and [forall a. a]? 如何投射`forall a。 a -&gt; a` 回到 `a -&gt; a`? - How to cast `forall a. a -> a` back to `a -> a`? 为什么函数(+)匹配类型(a - &gt; b - &gt; b)? - Why function (+) matches type (a -> b -> b)? 为什么加入。 (翻转fmap)有类型((A - &gt; B) - &gt; A) - &gt;(A - &gt; B) - &gt; B? - Why does join . (flip fmap) have type ((A -> B) -> A) -> (A -> B) -> B? foldl 类型中 `ta -&gt; b` 的含义 - Meaning of `t a -> b` in type of foldl (a == b)的类型能否派生为多态的? - Can the type of (a == b) be derived to be polymorphic?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM