for comprehension scala loops compiler-theory for-comprehension

comprehension - Scala "<-" para comprensión



for yield scala (3)

He descubierto que Scala siempre tiene una "explicación natural" para cualquier cosa. Siempre algo como "ohh, pero eso es solo una función que se llama en este y ese objeto con este y ese parámetro". En cierto sentido, nada es realmente compilador, como lo conocemos desde otros idiomas.

Mi pregunta es sobre el operador <- como se usa en el siguiente código:

for(i <- 0 to 10) println(i)

En este ejemplo, puedo ver que se reescribe a algo así como:

0.to(10).foreach((i:Int)=>println(i))

pero esto no explica cómo fui llevado a la función anónima dentro de la función foreach. En el punto donde escribes i, no es un objeto, y aún no es una variable declarada. Entonces, ¿qué es y cómo se transmite al interior de Foreach?

Creo que finalmente descubrí algo que, de hecho, es magia de compilación

Gracias por tu tiempo.

Para aclarar, mi pregunta es: ¿cómo funciona el operador <- en la primera línea de código ya que no es un objeto sobre el que se puede llamar como una función.


En este caso, realmente es un poco de magia de compilación. La traducción de for-comprehension a filter / map / flatmap form es un proceso especial de desugaring, muy parecido a la conversión de las formas especiales de actualización y aplicación de métodos.


Para aumentar la respuesta de Dave, aquí hay un esquema de traducción para ''for-comprehensions'' de la especificación del lenguaje Scala:

Una comprensión for (enums) yield e evalúa la expresión e para cada enlace generado por los enumeradores enumerados. Una secuencia de enumerador siempre comienza con un generador; esto puede ser seguido por generadores adicionales, definiciones de valores o guardias.

Un generador p <- e produce enlaces a partir de una expresión e que coincide de alguna manera con el patrón p . Una definición de valor val p = e vincula el nombre de valor p (o varios nombres en un patrón p ) al resultado de evaluar la expresión e . Un guardián if e contiene una expresión booleana que restringe los enlaces enumerados.

El significado preciso de generadores y guardias se define mediante la traducción a invocaciones de cuatro métodos: map , filter , flatMap y foreach . Estos métodos se pueden implementar de diferentes maneras para diferentes tipos de operadores.

El esquema de traducción es el siguiente. En un primer paso, cada generador p <- e , donde p no es irrefutable (§8.1) para el tipo de e se reemplaza por

p <- e.filter { case p => true; case _ => false }

Luego, las siguientes reglas se aplican repetidamente hasta que se hayan eliminado todas las comprensiones.

  • Una for-comprensión for (p <- e) yield e0 se traduce a e.map { case p => e0 } .

  • Una for-comprensión for (p <- e) e0 se traduce a e.foreach { case p => e0 } .

  • Una for-comprensión for (p <- e; p0 <- e0 . . .) yield e00 , donde. . . es una secuencia (posiblemente vacía) de generadores o guardias, se traduce a:
    e.flatMap { case p => for (p0 <- e0 . . .) yield e00 } .

  • A for-understanding for (p <- e; p0 <- e0 . . .) e00 donde. . . es una secuencia (posiblemente vacía) de generadores o guardias, se traduce a:
    e.foreach { case p => for (p0 <- e0 . . .) e00 } .

  • Un generador p <- e seguido de un guardia if g se traduce a un solo generador:
    p <- e.filter((x1, . . . , xn) => g )
    donde x1 . . , xn son las variables libres de p .

  • Un generador p <- e seguido de una definición de valor val p0 = e0 se traduce al siguiente generador de pares de valores, donde x y x0 son nombres nuevos:

    val (p, p0) <- for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }


<- es un símbolo de palabra clave definido por el idioma, como is => pero en claro contraste con -> (que es un símbolo definido). Debido a que es parte de la gramática básica de Scala, se puede usar para crear enlaces (para el i en su ejemplo) que es algo que no se puede hacer mediante construcciones definidas por el usuario.