为什么编译器将 Builder 中的逆变位置标记为不变?
Why contravariant position in Builder is marked as invariant by compiler?
我想了解为什么 scala 编译器会针对以下代码段发出错误:
import scala.collection.mutable
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
错误输出:
<console>:13: error: covariant type A occurs in invariant position in type => scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
^
据我了解,方差规则是这样从外到内推导出来的:
x
处于正位置。
mutable.Map.empty
类型参数处于中立位置。
- 由于
2.
和Builder[-Elem, +To]
的方差标注,A
处于负位置,C
处于正位置
因此,A
协变被错误地置于负(逆变)位置 -Elem
。为什么编译器说它是一个“不变位置”?
如果我使 A
不变,如:
class X[A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
错误变为:
On line 2: error: covariant type C occurs in invariant position in type scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
我认为 C
位置应该是正的(协变的),但编译器说它也是不变的?
这里的问题是您正在尝试使用 A 和 C 作为 val x 的类属。
编译器不知道要使用哪种类型。
您可以尝试以下方法:
trait A1
trait C1
class X[A <: A1, C <: C1] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
这将确保 A 和 C 来自您想要的类型。
因为Map
的值类型是不变的。值类型可能同时出现在协变位置(如 get
的 return 值)和逆变位置(如 +=
的右侧)。
如果编译器允许你继续,就会有类型安全问题。
class A0
class A1 extends A0
class A2 extends A0
class C0
class C1 extends C0
class C2 extends C0
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]] // won't compile
}
val x1: X[A1, C1] = new X[A1, C1]
val x2: X[A0, C0] = x1 // works because X[+A, +C]
x2.x += (new A2, new SomeBuilder[A0, C2]) // breaks type system
// we placed A2 as key, while the original x1 only accepts A1
// similar conflict happens for the value position
我想了解为什么 scala 编译器会针对以下代码段发出错误:
import scala.collection.mutable
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
错误输出:
<console>:13: error: covariant type A occurs in invariant position in type => scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
^
据我了解,方差规则是这样从外到内推导出来的:
x
处于正位置。mutable.Map.empty
类型参数处于中立位置。- 由于
2.
和Builder[-Elem, +To]
的方差标注,A
处于负位置,C
处于正位置
因此,A
协变被错误地置于负(逆变)位置 -Elem
。为什么编译器说它是一个“不变位置”?
如果我使 A
不变,如:
class X[A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
错误变为:
On line 2: error: covariant type C occurs in invariant position in type scala.collection.mutable.Map[Int,scala.collection.mutable.Builder[A,C]] of value x
我认为 C
位置应该是正的(协变的),但编译器说它也是不变的?
这里的问题是您正在尝试使用 A 和 C 作为 val x 的类属。 编译器不知道要使用哪种类型。
您可以尝试以下方法:
trait A1
trait C1
class X[A <: A1, C <: C1] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]]
}
这将确保 A 和 C 来自您想要的类型。
因为Map
的值类型是不变的。值类型可能同时出现在协变位置(如 get
的 return 值)和逆变位置(如 +=
的右侧)。
如果编译器允许你继续,就会有类型安全问题。
class A0
class A1 extends A0
class A2 extends A0
class C0
class C1 extends C0
class C2 extends C0
class X[+A, +C] {
val x = mutable.Map.empty[Int, mutable.Builder[A, C]] // won't compile
}
val x1: X[A1, C1] = new X[A1, C1]
val x2: X[A0, C0] = x1 // works because X[+A, +C]
x2.x += (new A2, new SomeBuilder[A0, C2]) // breaks type system
// we placed A2 as key, while the original x1 only accepts A1
// similar conflict happens for the value position