![](/img/trans.png)
[英]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?
[英]Why can't the type of id be specialised to (forall a. a -> a) -> (forall b. b -> b)?
在Haskell中采用简单的身份功能,
id :: forall a. a -> a
鉴于Haskell据说支持不可预测的多态性,我应该能够通过类型归属将id
“限制”到类型(forall a. a -> a) -> (forall b. b -> b)
a。a (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
没有任何区别。
为什么它期待一种类型
(forall a. a -> a) -> b -> b
我认为类型forall b.(forall a. a -> a) -> b -> b
等同于你给出的类型。 它只是它的规范表示,其中forall尽可能向左移动。
并且它不起作用的原因是给定类型实际上比id :: forall c的类型更多态。 c - > c,它要求参数和返回类型相等。 但是你的类型中的forall有效地禁止与任何其他类型统一。
你是绝对正确的forall b. (forall a. a -> a) -> b -> b
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. 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
。 对于不熟悉符号的人来说, Λ
是一个lambda,它将一个类型作为参数,与λ
不同,它以一个术语作为参数。
f
的调用者首先提供类型参数a
,然后提供类型参数b
,然后提供符合所选类型的两个值x
和y
。 需要注意的重要事项是呼叫者选择a
和b
。 因此调用者可以执行像f String Int length
这样的应用程序,例如生成一个术语String -> Int
。
使用-XRankNTypes
可以通过显式放置通用量词来注释术语,它不必位于最外层。 您的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
来将x
应用于0
和'e'
。
但在这种情况下,调用者不能像之前用f
那样选择类型参数。 你会注意到lambda中的应用程序x Int
和x Char
。 这会强制调用者提供多态函数,因此像g length
这样的术语无效,因为length
不适用于Int
或Char
。
考虑它的另一种方法是将f
和g
的类型绘制为树。 f
的树具有通用量词作为根,而g
的树具有箭头作为根。 为了获得f
的箭头,调用者实例化两个量词。 使用g
,它已经是箭头类型,调用者无法控制实例化。 这会强制调用者提供多态参数。
最后,请原谅我做作的例子。 Gabriel Scherer描述了更高等级多态性在System F的中等实际使用中对ML的更实际用途。 您还可以参考TAPL的第23和30章,或浏览编译器扩展的文档,以查找更高详细信息或更高级别多态性的实际示例。
我不是关于不可预测类型的专家,所以这既是一个潜在的答案,也是尝试从评论中学习一些东西。
专业化没有意义
\/ a . a -> a (1)
至
(\/ a . a -> a) -> (\/ b . b -> b) (2)
我不认为不可预测的类型是允许它的理由。 量词通常具有使(2)不等价集的左侧和右侧表示的类型的效果。 然而a -> a
in(1)意味着左侧和右侧是等价的集合。
例如,你可以将(2)具体化为(int - > int) - >(string - > string)。 但是通过任何系统,我知道这不是(1)所代表的类型。
错误消息看起来像是由Haskel类型推理器尝试统一id
的类型而产生的
\/ a . a -> a
与你给出的类型
\/ c . (c -> c) -> \/ d . (d -> d)
在这里,为了清晰起见,我将量化变量统一起来。
类型inferencer的任务是找到一个最普通的任务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)
- 即使你的操作也是不可能的类型会允许他们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.