Pregunta

¿Por qué esta construcción causa un error de tipo no coincidente en 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 cambio el Algunos con la lista que compila bien:

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

Esto también funciona bien:

for (first <- Some(1); second <- Some(2)) yield (first,second)
¿Fue útil?

Solución

Para comprensiones se convierten en llamadas al método map o flatMap. Por ejemplo éste:

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

se convierte en lo siguiente:

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

Por lo tanto, el primer valor de bucle (en este caso, List(1)) recibirá la llamada al método flatMap. Desde flatMap en un List vuelve otra List, el resultado de la comprensión por supuesto será un List. (Esto era nuevo para mí: Para comprensiones no siempre resultan en corrientes, ni siquiera necesariamente en Seqs.)

Ahora, echar un vistazo a cómo flatMap se declara en Option:

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

Tenga esto en cuenta. Vamos a ver cómo la errónea para la comprensión (el que tiene Some(1)) se convierte en una secuencia de llamadas mapa:

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

Ahora, es fácil ver que el parámetro de la llamada flatMap es algo que devuelve un List, pero no un Option, según sea necesario.

Con el fin de solucionar la cosa, puede hacer lo siguiente:

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

que compila bien. Vale la pena señalar que Option no es un subtipo de Seq, como a menudo se supone.

Otros consejos

Un consejo fácil de recordar, de comprensiones a tratar de devolver el tipo de la colección del primer generador, Opción [Int] en este caso. Por lo tanto, si usted comienza con Algunos (1) que debe esperar el resultado de la opción [T].

Si quieres un resultado de tipo, usted debe comenzar con un generador de lista.

¿Por qué tener esta restricción y no asumir que siempre querrá algún tipo de secuencia? Usted puede tener una situación en la que tiene sentido Option retorno. Tal vez usted tiene un Option[Int] que desea combinar con algo para conseguir un Option[List[Int]], dice con la siguiente función: (i:Int) => if (i > 0) List.range(0, i) else None; a continuación, usted podría escribir esto y obtener Ninguno cuando las cosas no "tiene sentido":

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

Como de comprensiones se expanden en el caso general son, de hecho, un mecanismo bastante general para combinar un objeto de tipo M[T] con un (T) => M[U] función para obtener un objeto de tipo M[U]. En su ejemplo, M puede ser opción o Lista. En general, tiene que ser el mismo tipo M. Así que no se puede combinar con la opción de lista. Para ejemplos de otras cosas que pueden ser M, vistazo a subclases de este rasgo .

¿Por qué la combinación de List[T] con el trabajo (T) => Option[T] aunque cuando comenzó con la lista? En este caso, la biblioteca de utilizar un tipo más general donde tiene sentido. Así se puede combinar con la lista de Traversable y hay una conversión implícita de Opción para de Traversable.

La conclusión es la siguiente: pensar en qué tipo desea que la expresión para regresar y empezar con ese tipo que el primer generador. Envolverlo en ese tipo si es necesario.

Probablemente tiene algo que ver con la opción de no ser un Iterable. El implícita Option.option2Iterable manejará el caso en que el compilador está a la espera segundos para ser un Iterable. Espero que la magia compilador es diferente en función del tipo de la variable de bucle.

Siempre encontraron útil esta información:

scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))

scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
   foo.flatten
       ^

scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))

scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)

scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top