user79074
user79074

Reputation: 5280

Cannot convert seq of parameterized types to a map

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

Answers (1)

flavian
flavian

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

Related Questions