scala for-loop type-mismatch for-comprehension scala-option

No coinciden los tipos en Scala for Comprehension



for-loop type-mismatch (4)

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

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

se convierte en eso:

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

Por lo tanto, el primer valor de ciclo (en este caso, List(1) ) recibirá la flatMap método flatMap . Como flatMap en una List devuelve otra List , el resultado de la comprensión será, por supuesto, una List . (Esto era nuevo para mí: las comprensiones no siempre resultan en transmisiones, ni siquiera necesariamente en Seq ).

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

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

Mantén esto en mente. Veamos cómo el erróneo para la comprensión (el que tiene Some(1) ) se convierte en una secuencia de llamadas a mapas:

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 una List , pero no una Option , como se requiere.

Para solucionarlo, puede hacer lo siguiente:

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

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

¿Por qué esta construcción causa un error de Tipo de coincidencia 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 Some with the List 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)


Probablemente tiene algo que ver con que Option no sea un Iterable. La Option.option2Iterable implícita 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.


Siempre encontré esto útil:

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)


Un consejo fácil de recordar, ya que las comprensiones intentarán devolver el tipo de la colección del primer generador, Opción [Int] en este caso. Entonces, si comienzas con Some (1) deberías esperar un resultado de la Opción [T].

Si desea un resultado del tipo de lista , 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 la Option . Tal vez tenga una Option[Int] que desee combinar con algo para obtener una 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 None cuando las cosas no "tengan 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 las 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 de 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 ejemplos de otras cosas que pueden ser M , mira las subclases de este rasgo .

¿Por qué la combinación de la 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.