ventajas tipos tecnicas subrayado reglas practicar para importancia estudio ejercicios ejemplo caracteristicas scala anonymous-function underscores partial-application

scala - tecnicas - tipos de subrayado pdf



¿Cuáles son las reglas para gobernar el subrayado para definir la función anónima? (2)

Estoy usando _ como marcador de posición para crear una función anónima, y ​​el problema es que no puedo predecir cómo Scala va a transformar mi código. Más precisamente, determina erróneamente qué tan "grande" es la función anónima que quiero.

List(1,2,3) foreach println(_:Int) //error ! List(1,2,3) foreach (println(_:Int)) //work List(1,2,3) foreach(println(_:Int)) //work

Usando -Xprint:typer Puedo ver que Scala transforma el primero en "una gran función anónima":

x$1 => List(1,2,3) foreach(println(x$1:Int))

el 2 ° 3 ° trabajado es la transformación correcta en lo que quiero.

... foreach (x$1 => println(x$1:Int))

¿Por qué esto? Cual es la regla ?


Creo que la respuesta del Sr. Sobral es incorrecta. Las reglas reales se pueden encontrar en Scala Language Reference , sección 6.23, subtítulo "Sintaxis del marcador de posición para funciones anónimas".

La única regla es que la expresión más interna que contiene correctamente el guión bajo define el alcance de la función anónima. Eso significa que las dos primeras reglas del Sr. Sobral son correctas, porque una llamada a un método es una expresión y la expresión entre paréntesis no cambia su significado. Pero la tercera regla es lo opuesto a la verdad: si todas las demás cosas son iguales, se usará la expresión más pequeña que tenga sentido.

Desafortunadamente, mi explicación del comportamiento que el Sr. Laskowski observó para su primer ejemplo es un poco complicado y especulativo. Cuando

List(1,2,3) foreach println(_:Int)

está escrito en el bucle Scala read-eval-print. El mensaje de error es:

error: type mismatch; found : Unit required: Int => ? List(1,2,3) foreach println(_:Int) ^

Si modifica el ejemplo un poquito:

List(1,2,3).foreach println(_:Int)

el mensaje de error es más fácil de entender:

error: missing arguments for method foreach in class List; follow this method with `_'' if you want to treat it as a partially applied function List(1,2,3).foreach println(_:Int) ^

Para entender un poco mejor las cosas, llame a scala manera: scala -Xprint:parser , que, después de que cada expresión sea tipeada por el usuario, hace que la expresión sea cargada por el analizador para ser impresa. (Junto con una gran cantidad de basura, que voy a omitir.) Para el primer ejemplo de Laskowski, la expresión que entiende el analizador es

((x$1: Int) => List(1, 2, 3).foreach(println((x$1: Int))))

Para el segundo ejemplo, la versión del analizador es

((x$1: Int) => List(1, 2, 3).foreach.println((x$1: Int)))

Aparentemente, la regla de alcance se aplica antes de que la estructura de expresiones se haya desarrollado completamente. En ambos casos, el analizador adivina que la expresión más pequeña comienza en Lista, aunque una vez que se insertan los parens, ya no es verdadera. En el segundo ejemplo, además de esa suposición, asume que, como println es un identificador, foreach println es una cadena de métodos, el primero no tiene argumentos. El error en foreach se println antes del error en println , enmascarándolo. El error en println es que su resultado es Unidad, y foreach requiere una función. Una vez que ve el árbol de análisis sintáctico, es fácil ver que esto es correcto, pero no está claro (para mí) por qué el árbol de análisis sintáctico es lo que es.


Reglas simples para determinar el alcance del guión bajo:

  1. Si el guión bajo es un argumento para un método, entonces el alcance estará fuera de ese método, de lo contrario las reglas a continuación;
  2. Si el guión bajo está dentro de una expresión delimitada por () o {}, se usará el delimitador más interno que contenga el guión bajo;
  3. En igualdad de condiciones, se usará la expresión más grande posible.

Entonces, según la regla # 1, en lugar de println((x: Int) => x) , el alcance se colocará fuera (incluido) println .

Por regla # 2, los dos últimos ejemplos tendrán la función delimitada por paréntesis, entonces (x => println(x: Int)) .

Según la regla n. ° 3, el primer ejemplo será la expresión completa, ya que no hay paréntesis delimitadores.