问题描述
为什么Foo
不变时以下代码为什么起作用,而当它协变时为什么不起作用? Foo
的协变版本会产生类型错误,表明在useF1
的调用中,参数的类型为Foo[T]
,但必须为F1
. useF2
也会产生类似的错误.
Why does the following code work when Foo
is invariant, but not when it is covariant? The covariant version of Foo
produces a type error saying that in the call of useF1
, the argument has type Foo[T]
but F1
is required. A similar error is produced for useF2
.
如果从Foo
中删除了方差注释,则该代码有效.与F1
的模式匹配揭示了T = Int
的事实,因此x
的类型为Foo[Int]
.隐式转换函数用于在useF1
的参数中将Foo[Int]
转换为F1
.对于F2
同样.当Foo是协变时,此过程的哪一部分不同,为什么?
If the variance annotation is removed from Foo
, the code works. The pattern match against F1
exposes the fact that T = Int
, so that x
has type Foo[Int]
. The implicit conversion function is used to convert Foo[Int]
to F1
in the argument of useF1
. Similarly for F2
. What part of this process is different when Foo is covariant, and why?
// A GADT with two constructors
sealed abstract class Foo[+T]
final case class F1() extends Foo[Int]
final case class F2() extends Foo[Unit]
object Example {
// A Foo[Int] can only be an F1
implicit def refineGADT(x : Foo[Int]) : F1 = x.asInstanceOf[F1]
// A Foo[Unit] can only be an F2
implicit def refineGADT(x : Foo[Unit]) : F2 = x.asInstanceOf[F2]
def useF1(x : F1) = ()
def useF2(x : F2) = ()
def demo[T](x : Foo[T]) = x match {
case F1() => useF1(x) // error
case F2() => useF2(x) // error
}
}
尽管GADT通常会使子类型化更加复杂,但在这种情况下,仅有的两种可能的具体类型是Foo[Int]
和Foo[Unit]
,它们之间没有子类型关系,因此子类型不会影响此示例.
Although GADTs make subtyping more complicated in general, in this case the only two possible concrete types are Foo[Int]
and Foo[Unit]
, and no subtyping relationship holds between them, so subtyping shouldn't affect this example.
推荐答案
首先,让我们简化您的示例(假设我们忽略类型擦除):
First, let's simplify your example (assuming we ignore type erasure):
class Foo[+T]
def demo[T](x : Foo[T]) = x match {
case _: Foo[Int] => x: Foo[Int] //error but works with `class Foo[T]`
case _: Foo[Unit] => x: Foo[Unit] //error but works with `class Foo[T]`
}
甚至:
class Foo[T]
scala> def demo[T](x : Foo[T]) = x match {case _: Foo[Int] => x}
demo: [T](x: Foo[T])Foo[Int] //notice Int
class Foo[+T]
scala> def demo[T](x : Foo[T]) = x match {case _: Foo[Int] => x}
demo: [T](x: Foo[T])Foo[T] //notice T
x
作为表达式的预期类型是存在性类型Foo[_ >: T]
(作为应用于返回类型的协方差的结果),或更准确地说,是Foo[X >: T] forSome{type X}
.因此,编译器无法处理此功能,因为此功能或错误(匹配上下文中的类型转换)不适用于存在类型,因为它无法证明Foo[Int]
始终属于R
,其中R :> Foo[X]
对于某些X >: Int
.因此,R
可能是:> Foo[Int]
或可能是:> Foo[Any]
或其他:> Foo[_ :> Int]
,这使R
成为可能范围的副产物.此类副产品不能转换为Foo[Int]
:
Expected type of x
as expression is the existential type Foo[_ >: T]
(as a result of covariance applied to the return type), or more precisely Foo[X >: T] forSome{type X}
. So compiler can't process it as this feature or bug (typecast in context of matching) doesn't work for existential types as it can't prove that Foo[Int]
always belongs to R
, where R :> Foo[X]
for some X >: Int
. So R
may be :> Foo[Int]
or may be :> Foo[Any]
or something else :> Foo[_ :> Int]
, which makes R
a coproduct of possible ranges. Such coproduct can't be cast to Foo[Int]
:
class Foo[T]
def demo(x : Foo[_]) = x match {
case _: Foo[Int] => x: Foo[Int] //error
case _: Foo[Unit] => x: Foo[Unit] //error
}
<console>:9: error: type mismatch;
found : Foo[_$1] where type _$1
required: Foo[Int]
case a: Foo[Int] => x: Foo[Int] //error
^
^
<console>:10: error: type mismatch;
found : Foo[_$1] where type _$1
required: Foo[Unit]
case a: Foo[Unit] => x: Foo[Unit] //error
^
P.S.有关协方差与存在性的关系的示例:
P.S. Example about how covariance relates to existentiality:
scala> class Foo[T]
defined class Foo
scala> def demo[T](x : Foo[T]) = (x: Foo[_ >: Int]) //can't cast to something in Int..Any range
<console>:17: error: type mismatch;
found : Foo[T]
required: Foo[_ >: Int]
Note: T <: Any, but class Foo is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
def demo[T](x : Foo[T]) = (x: Foo[_ >: Int])
^
scala> class Foo[+T]
defined class Foo
scala> def demo[T](x : Foo[T]) = (x: Foo[_ >: Int])
demo: [T](x: Foo[T])Foo[Any]
这篇关于具有协方差的隐式分辨率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!