2016-05-19 2 views
2

Je suis désolé pour un tel titre non descriptif, mais je ne sais vraiment pas comment l'exprimer mieux.Qu'est-ce qui ne va pas avec ça (bug du compilateur)?

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? Si j'utilise .mapValues au lieu de .map, cela fonctionne. Aussi, en faisant Foo covariant le corrige aussi, mais dans ce cas, je me retrouve avec Map[Int,Foo[Any]] Que se passe-t-il ici? Des idées?

+2

Quel type avez-vous * voulez * à la fin? – Bergi

+0

'Carte [Int, Foo [_]]' serait bon – Dima

Répondre

4

Sans variance, vous créez une séquence un peu "aucun sens":

class Foo[T] 
val in = Seq(new Foo[String], new Foo[Int]) // Seq[_ >: Int with String]] 

Il n'y a tout simplement pas LUB commun entre Foo[String] et Foo[Int]. Vous pouvez assigner un type existentiel:

val in = Seq[Foo[_]](new Foo[String], new Foo[Int]) 

Ensuite, nous pouvons essayer de continuer:

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) 

Encore une fois le type existentiel vous mord ici une inférence utile de rejeter pour le type de valeur. Vous pouvez l'appliquer de nouveau:

def p = m.toMap[Int, Foo[_]] // Map[Int,Foo[_]] 

AFAIK, Scalac ne déduira pas les types existentiels pour vous.


Si vous pensez que vous avez Foo[Any] ici, vous devez ajouter une annotation de la variance:

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]] 
+0

Je ne veux pas 'Foo [Any]', je veux un type existentiel. '.toMap [Int, Foo [_]]' fonctionne, merci ... c'est à peu près la seule option que je n'ai pas essayée :) Je ne comprends toujours pas pourquoi '.map' et' .mapValues' fonctionnent différemment (le ce dernier fonctionne, et je n'ai même pas besoin de '.toMap' - ce qui est redondant, parce que le résultat de' .map' et '.mapValues' est déjà une carte). En outre, pourquoi '.grouBy' fonctionne-t-il en premier lieu? Si le type les valeurs si connu après 'groupBy', puis mappant les valeurs à la tête de seq ne devrait pas faire pire, devrait-il? – Dima