In the example below, I'm wondering why funPoly
can't accept the existentially quantified type value outersFromInnersEx
, even though funEx
can.
case class InnerCassClass[I, E, O](i: I, e: E, o: O)
case class OuterCaseClass[I, E, O](inner: InnerCassClass[I, E, O])
val inner1 = InnerCassClass(5, "foo", 3.3f)
val inner2 = InnerCassClass(4.4f, 6, "bar")
// Doesn't work as expected due to invariance of type paramemters I, E, O (but confirm)
// val outersFromInnersAny: List[OuterCaseClass[Any, Any, Any]] = List(inner1, inner2).map(OuterCaseClass.apply)
val outersFromInnersEx: List[OuterCaseClass[_, _, _]] = List(inner1, inner2).map(OuterCaseClass.apply)
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
// This doesn't work, but why?
val u1 = funPoly(outersFromInnersEx)
val u2 = funEx(outersFromInnersEx)
Note, I test this in Scala 3 (try online), but largely the problem is the same in Scala 2, though this particular example has other issues in Scala 2.
Note that these two are very different types:
def funPoly[I, E, O](occ: List[OuterCaseClass[I, E, O]]): Unit = ()
def funEx(occ: List[OuterCaseClass[_, _, _]]): Unit = ()
The first one is able to handle uniform Lists of OuterCaseClass
objects where the type parameters are the same for every object. The second one can handle mixed Lists of OuterCaseClass
objects where the type parameters are (potentially) different for every object.
Making the type parameters covariant “fixes” the problem because then List[OuterCaseClass[_, _, _]]
is equivalent to List[OuterCaseClass[Any, Any, Any]]
, and you can simply instantiate funPoly
s type parameters to Any, Any, Any
to make it accept anything that funEx
can.
Generally speaking, you can pass existentially qualified types to polymorphic functions. For example, this should work:
case class ListAndFunction[A](list: List[A], function: A => Int)
val a: ListAndFunction [_] = ListAndFunction[String](List("a"), _.length)
def mapFunction[A](a: ListAndFunction [A]): List[Int] =
a.list.map(a.function)
mapFunction(a)