0

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?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
SwiftMango
  • 15,092
  • 13
  • 71
  • 136

1 Answers1

2

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)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks that makes sense. Accepted as an answer, but I got other problem: https://stackoverflow.com/questions/57733018/how-to-return-wildcard-generic – SwiftMango Aug 30 '19 at 20:45