为什么Foo
不变时,以下代码有效,但是当它是协变时却不行? Foo
的协变版本会产生类型错误,表示在useF1
的调用中,参数的类型为Foo[T]
,但F1
是必需的。为useF2
生成了类似的错误。
如果从Foo
中删除了方差注释,则代码可以正常工作。与F1
匹配的模式暴露了T = Int
的事实,因此x
具有类型Foo[Int]
。隐式转换函数用于在Foo[Int]
的参数中将F1
转换为useF1
。同样适用于F2
。当Foo是协变的时,这个过程的哪个部分是不同的,为什么?
// 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]
,并且它们之间没有子类型关系,所以子类型不应该影响这个例子
答案 0 :(得分:0)
您只需重新绑定F1
中匹配的F2
或demo
:
def demo[T](x : Foo[T]) = x match {
case y@F1() => useF1(y)
case y@F2() => useF2(y)
}
x
的类型为Foo[T]
,因此编译器无法推断它是useF1
的有效输入。当T
不变时,编译器可以推断出更多,并且能够解决这种情况。当T
是协变的时,我们知道它在匹配的情况下是有效的,但我们必须绑定一个新的标识符来帮助编译器。您甚至可以将匹配的类绑定到x
以遮蔽外部x
(例如case x@F1()
),但如果您需要,则无法引用外部x
答案 1 :(得分:0)
首先,让我们简化你的例子(假设我们忽略了类型擦除):
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]
1}}对于某些X >: Int
。因此R
可能是:> Foo[Int]
,也可能是:> Foo[Any]
或其他:> Foo[_ :> Int]
,这使R
成为可能范围的副产品。此类副产品无法转换为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。关于协方差如何与存在性相关的例子:
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]