问题
I have an extential type of tuple, and I want to pass it to a generic function (I use ClassTag in this example but it is a custom type class with invariant type parameter):
type TC = (ClassTag[T], Option[T]) forSome {type T}
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
val tc: TC = (classTag[String], Option[String](null))
foo(tc._1, tc._2)
This gives me error:
error: type mismatch; found : scala.reflect.ClassTag[T] required: scala.reflect.ClassTag[Any] Note: T <: Any, but trait ClassTag is invariant in type T. You may wish to investigate a wildcard type such as
_ <: Any
. (SLS 3.2.10) foo(tc._1, tc._2)
I wanna ensure the type of two parameters ct
and o
uses the same parameter type T and I thought extential type should ensure this, but it does not seem to work.
However if I don't use tuple and only use ClassTag, it works fine:
type TC = ClassTag[T] forSome {type T}
def foo[T](ct: ClassTag[T]) = {}
val tc: TC = classTag[String]
foo(tc)
So the previous error of ClassTag invariance does not make sense.
Why does it not work when I use a tuple? How can I make it work?
回答1:
For val tc: TC = classTag[String], Option[String](null))
tc._1
has type ClassTag[_]
(which is different from any ClassTag[T]
e.g. ClassTag[Any]
because ClassTag
is invariant), tc._2
has type Option[_]
, which is the same as Option[Any]
because Option
is covariant
implicitly[Option[_] =:= Option[Any]]
implicitly[Option[Any] =:= Option[_]]
An existential type
𝑇 forSome { 𝑄 }
where𝑄
contains a clause type𝑡[tps]>:𝐿<:𝑈
is equivalent to the type𝑇′ forSome { 𝑄 }
where𝑇′
results from𝑇
by replacing every covariant occurrence of𝑡
in𝑇
by𝑈
and by replacing every contravariant occurrence of𝑡
in𝑇
by𝐿
.
https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#existential-types
So in
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
foo(tc._1, tc._2)
we have the type mismatch. Any
and _
(aka arbitrary T
) are different types.
Try to use universal quantification rather than existential
type TC[T] = (ClassTag[T], Option[T])
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
val tc: TC[String] = (classTag[String], Option[String](null))
foo(tc._1, tc._2)
来源:https://stackoverflow.com/questions/57731598/how-to-pass-extential-type-of-tuple-to-invariant-generic-function