Scala 类型成员差异

Scala type members variance

考虑这个简短的片段:

trait Table[+A] {
  type RowType = Seq[A]
}

Scala 2.11.7 编译器给出以下错误:

covariant type A occurs in invariant position in type Seq[A] of type RowType

为什么ASeq[A]中被认为是不变的位置,而Seq本身被定义为trait Seq[+A]

此外,如果我们忽略错误,您能否提供一个用例来说明此类型定义可能存在的问题?

对于任何 B <: A,您的 Table[B]#RowType 将比 Table[A]#RowType 更具体。更具体并不意味着相同,因此编译器正在将类型别名的参数视为不变位置。

如何解决这个问题。

抽象成员

你可以将你的类型定义为抽象类型,这意味着你应该稍后定义它并且可能会遇到同样的问题,但在 trait Table 级别上这样的定义是正确的

trait Table[+A] {
  type RowType <: Seq[A]
}

具体的高级类型

您可以定义参数化类型成员,这可能会导致更改您使用此类型的方式,但在大多数情况下应该可以完成这项工作。

trait Table[+A] {
  type RowType[+X] = Seq[X]
}

关于类型成员差异

我的领域不是最强的,但我试着描述我的想法。

假设你有

trait Table[+A] {
  type RowType = Seq[A]
}

def mkTable[A]: Table[A]  = new Table[A] {}

然后你做以下事情

val tupleTable = mkTable[(String, String)]
val prodTable: Table[Product] = tupleTable 

那么 prodTable.RowType 是什么?

根据您的定义,它应该是 Seq[Product]。但是等等,prodTabletupleTable是同一个对象,所以他们的成员应该是一样的,所以prodTable.RowType应该是Seq[(String, String)]

但是如果你换成第一种方法,比如

trait Table[+A] {
  type RowType <: Seq[A]
}

def mkTable[A]: Table[A]  = new Table[A] {
  type RowType = Seq[A]
}

编译器会知道 RowType 对于 Table[Product] 是某种类型 <: Seq[Product] 这对于 Seq[(String, String)] 是正确的并且所有歧义都消失了。

这是一个反例:

import scala.annotation.unchecked.uncheckedVariance

class Foo[+S](x: S) {
  type Shape = S @uncheckedVariance // THIS IS BAD
}
object Test {
  def main(args: Array[String]): Unit = {
    val fs: Foo[String] = new Foo("")
    
    def cast(x: Foo[Any]): x.Shape = 1 // This typechecks since x.Shape =:= Any
    
    val s: String = cast(fs) // This typechecks because Foo[String] <: Foo[Any], but results in a ClassCastException
    
  }
}

抛出:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Test$.main(main.scala:12)

斯卡斯蒂 link:https://scastie.scala-lang.org/smarter/Cno5sLZyTSybDC80uDt2hQ。感谢@smarter。