繁体   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)?

在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 ,然后提供符合所选类型的两个值xy 需要注意的重要事项是呼叫者选择ab 因此调用者可以执行像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 Intx Char 这会强制调用者提供多态函数,因此像g length这样的术语无效,因为length不适用于IntChar

考虑它的另一种方法是将fg的类型绘制为树。 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的任务是找到一个最普通的任务acd使得两个表达式是语法相等。 它最终发现需要统一cd 由于它们是单独量化的,所以它处于死路并退出。

你可能会问这个问题,因为基本类型推理器 - 带有归属(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM