覆盖子类型的差异

时间:2014-03-20 12:33:57

标签: scala higher-kinded-types

有人可以向我解释为什么示例1编译但示例2没有编译?

示例1:

trait Foo[+A]
trait Bar[A] extends Foo[A]

示例2:

trait Foo[A[+_]]
trait Bar[A[_]] extends Foo[A]

示例2没有使用以下错误消息进行编译:"类型参数(A)的种类不符合特征Foo中类型参数(类型A)的预期种类。 A类型参数与A类预期参数不匹配:类型_(in  trait Bar)是不变的,但是_(在trait Foo中)被声明为covariant"

2 个答案:

答案 0 :(得分:7)

在示例1中, +A不是对A 的约束。任何类型都可以是Foo的参数。这是Foo的约束。这意味着在Foo的界面中,A只能出现在协变位置(很快,它可能是方法结果,但不是方法参数)。

使Bar不协变意味着Bar的接口不满足相同的约束(或者至少不会通告它),因此可能在Bar中,A的方法}参数已添加。这很常见。例如,有collection.Seq[+A],它由collection.mutable.Seq[A]扩展。这没有任何问题。如果Y <: XBar[Y]不是Bar[X],但它仍然是Foo[X]

另一方面,在示例2中, A[+_]是对A 的约束。只有具有协变类型参数的类型可以是Foo的参数。 Foo的代码很可能会使用该约束,例如A[String]代码中A[Any]到某Foo的代理。

然后允许Bar使用非协变类型进行实例化将是不健全的。当Foo不再协变时,仍可以调用从A[String]继承的代码并将A[Any]分配给A

当您允许解除对泛型参数的约束时,会发生非常类似的健全性问题。假设您有Foo[X <: Ordered[X]](或Foo[X : ordering]),并且sort中有Foo方法。假设允许Bar[X] extends Foo。也许Bar的代码中没有任何内容需要X进行排序,但仍然可以调用sort,并且肯定会对无法订购的项目行为不端。


关于您在Traversable [+ Elem,+ Col [+ _]]的评论中的问题,并将其扩展为make a mutable class:

技术上,是的,您可以扩展它并在其中加入一些var youCanMutateThat : Elem = _。我猜这不是你想要的。但我想你的计划是允许使用带有可变Col的扩展,我认为你不能这样做,原因如上所述。但是,为什么你首先要有Col [+ _]约束?

答案 1 :(得分:2)

在示例2中,需要为A [_]

添加covarince
trait Bar[A[+_]] extends Foo[A]

因为 Foo 期望协变类型作为参数(+ _是替换类型的约束的一部分),并且继承类型需要保证参数将是协变的(取代类型的限制)。

在示例1中,您定义 Foo (如容器)是参数的协变,并且继承的容器可以是不变的(对替换类型没有限制)

Martin-Löf类型理论的更多细节(预测参数多态性