Reputation: 40508
I am sorry for such non-descriptive title, but I really don't know how to express this better.
class Foo[T]
Seq(new Foo[String], new Foo[Int]).groupBy(_ => 1).map { case (k, Seq(v)) => k -> v }.toMap
<console>:12: error: Cannot prove that (Int, Foo[_146]) forSome { type _146 >: Int with String } <:< (T, U).
WTF?
If I use .mapValues
instead of .map
, it works. Also, making Foo
covariant fixes it too, but in that case I end up with Map[Int,Foo[Any]]
What's going on here? Any ideas?
Upvotes: 2
Views: 77
Reputation: 67330
Without variance, you create a somewhat "nonsensical" sequence:
class Foo[T]
val in = Seq(new Foo[String], new Foo[Int]) // Seq[_ >: Int with String]]
There is simply no common LUB between Foo[String]
and Foo[Int]
. You may assign it an existential type:
val in = Seq[Foo[_]](new Foo[String], new Foo[Int])
Then we can try to continue:
val in = Seq[Foo[_]](new Foo[String], new Foo[Int])
val g = in.groupBy(_ => 1) // Map[Int, Seq[Foo[_]]]
// the next line would produce a match error, thus make it a `def`
def m = g.map { case (k, Seq(v)) => k -> v } // Iterable[(Int, Foo[_])]
def p = m.toMap // cannot prove that (Int, Foo[_]) <:< (T, U)
Again the existential type bites you here in disallowing a useful inference for the value type. You can enforce it again:
def p = m.toMap[Int, Foo[_]] // Map[Int,Foo[_]]
AFAIK, Scalac will not infer existential types for you.
If you are thinking you have Foo[Any]
here, you need to add a variance annotation:
class Foo[+T]
val in = Seq(new Foo[String], new Foo[Int]) // Seq[Foo[Any]]
def m = in.groupBy(_=>1).map {case (k,Seq(v)) => k->v}.toMap // Map[Int,Foo[Any]]
Upvotes: 4