Reputation: 5280
Why does the following code:
case class Test[E](name: String, e: E)
val seq = Seq[Test[_]]()
val map = seq.map(i => i.name -> i).toMap
Give me this compiler error:
Cannot prove that (String, Test[_$1]) forSome { type _$1 } <:< (T, U)
Upvotes: 4
Views: 94
Reputation: 28511
Your two options are:
case class Test[E](name: String, e: E)
val seq = Seq[Test[_]]()
val map = seq.map(i => i.name -> i).toMap[String, Test[_]]
Or you can also "inform" the compiler about the map type ahead of time which also works.
val seq = Seq.empty[Test[_]]
val map: Map[String, Test[_]] = seq.map(i => i.name -> i).toMap
Why this doesn't work
As to why this doesn't work, it really should, but what happens is Scala uses an implicit to determine the inner type of a collection M[X] <: TraversableOnce[X]
is a tuple, and that's found in the toMap
signature.
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U]
The part you care about is A <:< (T, U)
, which is saying A
is actually a subtype of Tuple2[T, U]
where T
should become the keytype and U
the value type. A
is the original underlying type of the collection.
Somehow the compiler is not smart enough to infer (T, U)
unless you explicitly provided, and it seems it doesn't have anything to do with the existential type either.
In theory you should be able to convert any traversable to a map easily, and we can reconstruct what happens when you call toMap
using higher kinds.
implicit class TraversableOps[
// We are saying here every M is a higher kinded collection type
// that extends TraversableOnce, like Map, Seq, and so on.
M[A] <: TraversableOnce[A],
T
](val col: M[T]) {
// Proof that T is actually a tuple2 type.
def customToMap[K, V](implicit ev: T <:< (K, V)): Map[K, V] = {
...
}
}
Upvotes: 3