Dima
Dima

Reputation: 40508

What's wrong with this (compiler bug)?

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

Answers (1)

0__
0__

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

Related Questions