为什么下限类型会改变方差位置?

时间:2014-05-30 15:39:21

标签: scala generic-variance

Scala Language Specification (Section 4.5 on Variance Annotations, p. 44)

  • 类型参数的方差位置与方差位置相反 封闭式参数条款。
  • 类型声明或类型参数的下限的方差位置 与类型声明或参数的方差位置相反。

使用上面的第一点,很容易看到(至少正式)

trait Covariant[+A] {
  def problematic[B <: A](x : B)
}

生成错误消息

error: covariant type A occurs in contravariant position in type >: Nothing <: A of type B
       def problematic[B <: A](x : B)

使用第一点和第二点很容易看出

trait Contravariant[-A] {
  def problematic[B >: A](x : B)
}

生成错误消息

error: contravariant type A occurs in covariant position in type >: A <: Any of type B
             def problematic[B >: A](x : B)

正如我所提到的,很容易正式看到(即遵循方差注释规则)为什么会出现这些错误。但是,我无法想出一个说明需要这些限制的例子。相比之下,很容易想出说明为什么方法参数应该改变方差位置的例子,参见例如。 Checking Variance Annotations

所以,我的问题如下:假设,上面的两段代码被允许,出现问题的例子是什么?这意味着,我正在寻找示例similar to this one来说明在上面引用的两条规则未被使用的情况下可能出现的问题。我对涉及较低类型边界的示例特别感兴趣。

请注意,Scala type bounds & variance的答案会打开这个特定问题,而The "lower bound" will reverse the variance of a type, but why?中给出的答案对我来说似乎不对。

编辑:我认为第一种情况可以如下处理(适应上面引用的例子)。假设,允许以下内容

trait Queue[+T] {
  def head : T
  def tail :  Queue[T]
  def enqueue[U <: T](x : U) : Queue[T]
}

然后我们可以实现

class QueueImplementation[+T] extends Queue[T] {
  /* ... implement Queue here ... */
}

class StrangeIntQueue extends QueueImplementation[Int] {
  override def enqueue[U <: Int](x : U) : Queue[Int] = {
    println(math.sqrt(x))
    super.enqueue(x)
  }
}

并将其用作

val x : Queue[Any] = new StrangeIntQueue
x.enqueue("abc")

这显然很麻烦。但是,我看不出如何调整这个以显示组合&#34;逆变类型参数+下限类型绑定&#34;也有问题吗?

2 个答案:

答案 0 :(得分:1)

使用++中的List方法查看为何需要限制。请注意,这需要++生成List[B]

 def ++[B](that: GenTraversableOnce[B]): List[B] 

带有

的完整签名
 def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

为什么[B >: A]重要。好吧,如果我们想要结合这样的东西

怎么办?
 trait Foo
 trait Bar extends Foo

我们有一个带签名的方法

 def op(that: List[Foo], other: Foo): List[Foo] = that ++ List(other)

我可以将Bar类型的列表传递给它,但为了能够将其作为List[Foo]返回,我必须使条件为Foo >: Bar以便我能够实际执行以下

 def see(that: List[Bar]): List[Foo] = op(that, myFoo)

基本上正在执行List[Bar] ++ List[Foo]以返回List[Foo]类型,如List[Foo]类型所表示的那样。这就是翻转发生的原因。

现在,如果我试图强制执行Foo <: Bar,我会立即遇到List[Bar] ++ List[Foo]无法返回类型Foo的列表的问题(更不用说它与定义冲突了)上面。)它只能返回最小上限的List

答案 1 :(得分:1)

假设我们允许某个类具有类型参数[-T],并且该类的方法具有[U >: T] ...

for come class hierarchy
Dog <: Mammal <: Animal

class Contra[-X](x: X){
  def problem[Y >: X](y: Y): Y = x // X<:Y so this would be valid
}

val cMammal:Contra[Mammal] = new Contra(new Mammal)

val a:Animal = cMammal problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cMammal problem new Mammal // Mammal >: Mammal, this is fine
val d:Mammal = cMammal problem new Dog    // (Dog upcasts to Mammal) >: Mammal, this is fine

val cDog:Contra[Dog] = cMammal // Valid assignment

val a:Animal = cDog problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cDog problem new Mammal // Mammal >: Mammal, this is fine
val d:Dog    = cDog problem new Dog    // AAAHHHHHHH!!!!!!

最后一行会输入检查,cDog problem new Dog实际上会返回Mammal。这显然不是一件好事。值得庆幸的是,类型系统实际上并没有让我们这样做。

Q.E.D。逆变类型参数+低级类型绑定不是一个好主意混合。

我希望这个例子有所帮助。