86 votes

Type incompatibilité sur Scala pour la compréhension

Je ne comprends pas pourquoi cette construction provoque une erreur de type incompatibilité dans Scala:

 for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

<console>:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
 

Si je commute le Some avec la List, ça compile bien:

 for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))
 

Cela fonctionne aussi très bien:

 for (first <- Some(1); second <- Some(2)) yield (first,second)
 

Cela n'a vraiment aucun sens pour moi, peut-être que je n'avais pas bien compris comment fonctionnent les compréhensions à Scala ...

Merci d'avance.

120voto

Madoc Points 3217

Pour les compréhensions sont convertis en appelle à l' map ou flatMap méthode. Par exemple celui-ci:

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

devient:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

Par conséquent, la première boucle de la valeur (dans ce cas, List(1)) recevra l' flatMap appel de la méthode. Depuis flatMap sur List renvoie un autre List, le résultat de la compréhension sera bien sûr un List. (Ce qui était nouveau pour moi: Pour les interprétations ne sont pas toujours le résultat dans les cours d'eau, même pas forcément en Seqs).

Maintenant, jetez un oeil à combien de flatMap est déclaré en Option:

def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

Gardez cela à l'esprit. Nous allons voir comment la erronée pour la compréhension (l'un avec des Some(1)) est converti en une séquence d'appels map:

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

Maintenant, il est facile de voir que le paramètre de l' flatMap appel est quelque chose qui renvoie un List, mais pas un Option, tel que requis.

Afin de réparer la chose, vous pouvez effectuer les opérations suivantes:

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

Qui compile très bien. Il est intéressant de noter que, Option n'est pas un sous-type d' Seq, comme on le suppose souvent.

34voto

huynhjl Points 26045

Facile rappelez-vous, pour des compréhensions va essayer de retourner le type de la collection du premier générateur, l'Option[Int] dans ce cas. Ainsi, si vous commencez avec Quelques(1) vous devez vous attendre à la suite de l'Option[T].

Si vous voulez un résultat de Liste type, vous devriez commencer avec un générateur de Liste.

Pourquoi cette restriction et de ne pas supposer que vous aurez toujours besoin d'une certaine sorte de séquence? Vous pouvez avoir une situation où il est logique de retour Option. Peut-être que vous avez un Option[Int] que vous souhaitez combiner avec quelque chose pour obtenir un Option[List[Int]],- dire avec la fonction suivante: (i:Int) => if (i > 0) List.range(0, i) else None; vous pouvez ensuite écrire cela et ne rien obtenir si les choses ne pas "faire sens":

val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns:  Option[List[Int]] = None

Comment faire pour les interprétations sont développés dans le cas général, sont en fait assez mécanisme général de combiner un objet de type M[T] avec une fonction (T) => M[U] pour obtenir un objet de type M[U]. Dans votre exemple, M peut être l'Option ou de la Liste. En général, il doit être le même type M. Donc vous ne pouvez pas combiner avec Option de la Liste. Pour des exemples d'autres choses qui peuvent être M, regarder les sous-classes de ce trait de caractère.

Pourquoi combinant List[T] avec (T) => Option[T] travail même si, lorsque vous avez commencé avec la Liste? Dans ce cas, l'utilisation de la bibliothèque d'un type plus général où il fait sens. De sorte que vous pouvez combiner Liste avec Traversable et il y a une conversion implicite de l'Option de Traversable.

La ligne de fond est: est-ce penser à ce type que vous voulez l'expression de retourner et de commencer avec ce type comme le premier générateur. L'envelopper dans ce type si nécessaire.

5voto

sblundy Points 27163

Cela a probablement quelque chose à voir avec Option n'étant pas un Itérable. Le Option.option2Iterable implicite gèrera le cas où le compilateur s'attend à ce que la seconde soit un Iterable. Je suppose que la magie du compilateur est différente selon le type de variable de boucle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X