2011-01-18 16 views
64

¿Por qué esta construcción causa un error de tipo No coinciden en Scala?No coincide el tipo en Scala For Comprehension

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) 
+2

¿Qué resultado esperaba que Scala regrese en el ejemplo anómalo? –

+0

Cuando estaba escribiéndolo, pensé que obtendría una Opción [Lista [(Int, Int)]]. –

Respuesta

99

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 que:

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 flatMap método. Desde flatMap en un List devuelve otro List, el resultado de la para la comprensión será, por supuesto, un List. (Esto era nuevo para mí: Para comprensiones no siempre resultan en corrientes, ni siquiera necesariamente en Seq s.)

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

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

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

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 es Option, según sea necesario.

Para fijar 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.

4

Probablemente tiene algo que ver con que Option no sea un Iterable. El Option.option2Iterable implícito manejará el caso donde el compilador espera que el segundo sea un Iterable. Espero que la magia del compilador sea diferente según el tipo de variable de ciclo.

24

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

Si desea un resultado de tipo List, debe comenzar con un generador de listas.

¿Por qué tiene esta restricción y no asume que siempre querrá algún tipo de secuencia? Puede tener una situación en la que tenga sentido devolver Option.Tal vez tenga un Option[Int] que desee combinar con algo para obtener un Option[List[Int]], digamos con la siguiente función: (i:Int) => if (i > 0) List.range(0, i) else None; entonces podría escribir esto y obtener Ninguno cuando las cosas no "tienen 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 

Cómo de comprensiones se expanden en el caso general son de hecho un mecanismo bastante general para combinar un objeto de tipo M[T] con una función (T) => M[U] para obtener un objeto del tipo M[U]. En su ejemplo, M puede ser Opción o Lista. En general, tiene que ser del mismo tipo M. Entonces no puedes combinar Option with List. Para ver ejemplos de otras cosas que pueden ser M, consulte subclasses of this trait.

¿Por qué la combinación de List[T] con (T) => Option[T] funcionó cuando comenzó con la lista? En este caso, la biblioteca usa un tipo más general donde tiene sentido. Entonces puede combinar List with Traversable y hay una conversión implícita de Option a Traversable.

La conclusión es la siguiente: piense en qué tipo desea que vuelva la expresión y comience con ese tipo como primer generador. Envuélvalo en ese tipo si es necesario.

Cuestiones relacionadas