为什么方法和类型对方差施加不同的约束?

问题描述 投票:3回答:1

我很困惑为什么scala在计算方差约束时会对deftype有不同的看法?

我试图打破我的文本墙,并将我的返回类型分解为一个单独的类型声明,并立即得到一个方差错误。这是一个最小化的例子:

class Bad[+A] {
  type Check[B >: A] = Bad[B]
  def check[B >: A] : Check[B] = this
}

失败了:

covariant.scala:2: error: covariant type A occurs in invariant position in type  >: A of type B
  type Check[B >: A] = Bad[B]
             ^
one error found

不过,它没有额外的类型声明它工作正常:

class Good[+A] {
  def check[B >: A]: Good[B] = this
}

class Ugly[+A] {
  type Check[B >: A @scala.annotation.unchecked.uncheckedVariance] = Ugly[B]
  def check[B >: A] : Check[B] = this
}
scala covariance
1个回答
2
投票

这是因为只要类型成员Check逃逸到外部,它就会立即出现在函数和方法的共同和逆变位置:

class Bad[+A] {
  type Check[B]
}

class B
class A extends B

val bad: Bad[A] = ???
import bad._

def hypotheticalMethodSomewhereOutside1(c: Check[B]) = ???
def hypotheticalMethodSomewhereOutside2(i: Int): Check[B] = ???

[B >: A]方法中check类型参数的关键区别

def check[B >: A]: Good[B] = this

是单一方法check在你的控制之下,你可以保证它不会在非协变位置使用A

与此相反,类型成员Check可以出现在无变量和逆变位置的无限多种其他方法中,并且这些方法不在您的控制之下,因此您不能禁止在Check看起来不协变的位置使用A ,例如你不能阻止上面例子中的hypotheticalMethodSomewhereOutsideN被其他人定义。


请注意,check示例失败时,Bad方法的存在不是必需的:

// error: covariant type A occurs in invariant position in type  >: A of type B
class Bad[+A] {
  type Check[B >: A] = Bad[B]
  // def check[B >: A] : Check[B] = this
}

但是,如果你隐藏每个人的成员类型Check(真的是每个人,包括同一个类的其他实例,也就是说,使用极其隐蔽的private[this]访问修饰符):

class Bad[+A] {
  private[this] type Check[B >: A] = Bad[B]
}

它汇编得很好。

private[this]解决了这个问题,因为使用这个修饰符,只需要检查类中的方法,不必考虑外面某处的假设方法:

class Ok[+A] {
  private[this] type Check[B >: A] = Ok[B]
  def ok[B >: A](c: Check[B]): Unit = ???
  def alsoOk[B >: A]: Check[B] = ???
}

注意,上面的内容可以写成

class Good[+A] {
  type Check[+B] = Good[B]
  def ok[B >: A](c: Check[B]): Unit = ???
  def alsoOk[B >: A]: Check[B] = ???
}

只有当类型构造函数Good具有比Check更多的参数时才有意义。

© www.soinside.com 2019 - 2024. All rights reserved.