tutorial learn koans exercises codecademy syntax scala functional-programming

syntax - koans - scala learn



¿Por qué Scala aplica thunks automáticamente, a veces? (4)

Justo después de 2:40 en el ShadowofCatron Scala Tutorial 3 de ShadowofCatron , se señala que los paréntesis que siguen al nombre de un thunk son opcionales . "Buh?" dijo mi cerebro de programación funcional, ya que el valor de una función y el valor que evalúa cuando se aplica son cosas completamente diferentes.

Así que escribí lo siguiente para probar esto. Mi proceso de pensamiento se describe en los comentarios.

object Main { var counter: Int = 10 def f(): Int = { counter = counter + 1; counter } def runThunk(t: () => Int): Int = { t() } def main(args: Array[String]): Unit = { val a = f() // I expect this to mean "apply f to no args" println(a) // and apparently it does val b = f // I expect this to mean "the value f", a function value println(b) // but it''s the value it evaluates to when applied to no args println(b) // and the application happens immediately, not in the call runThunk(b) // This is an error: it''s not println doing something funny runThunk(f) // Not an error: seems to be val doing something funny } }

Para aclarar el problema, este programa Scheme (y el volcado de consola que sigue) muestra lo que esperaba que hiciera el programa Scala.

(define counter (list 10)) (define f (lambda () (set-car! counter (+ (car counter) 1)) (car counter))) (define runThunk (lambda (t) (t))) (define main (lambda args (let ((a (f)) (b f)) (display a) (newline) (display b) (newline) (display b) (newline) (runThunk b) (runThunk f)))) > (main) 11 #<procedure:f> #<procedure:f> 13

Después de visitar este sitio para preguntar sobre esto, encontré esta respuesta que me dijo cómo corregir el programa Scala anterior:

val b = f _ // Hey Scala, I mean f, not f()

Pero el guión bajo ''pista'' solo se necesita a veces . Cuando llamo a runThunk(f) , no se requiere ninguna pista. Pero cuando ''alias'' f to b con un val luego lo aplico, no funciona: la aplicación sucede en el val ; e incluso la lazy val funciona de esta manera, por lo que no es el punto de evaluación que causa este comportamiento.

Todo eso me deja con la pregunta:

¿Por qué Scala a veces aplica Thunks automáticamente cuando los evalúa?

¿Es, como sospecho, inferencia de tipo? Y si es así, ¿no debería un sistema de tipos mantenerse fuera de la semántica del lenguaje?

¿Es esta una buena idea? ¿Aplican Thala los programadores de Scala en lugar de referirse a sus valores con tanta mayor frecuencia que hacer que el parens opcional sea mejor en general?

Ejemplos escritos utilizando Scala 2.8.0RC3, DrScheme 4.0.1 en R5RS.


El problema está aquí:

¿Buh? ", Dijo mi cerebro de programación funcional, ya que el valor de una función y el valor que evalúa cuando se aplica son cosas completamente diferentes.

Sí, pero no declaraste ninguna función.

def f(): Int = { counter = counter + 1; counter }

Declaró un método llamado f que tiene una lista de parámetros vacía y devuelve Int . Un método no es una función, no tiene un valor. Nunca jamás. Lo mejor que puedes hacer es obtener una instancia de Method través de la reflexión, que en realidad no es lo mismo.

val b = f _ // Hey Scala, I mean f, not f()

Entonces, ¿qué significa f _ ? Si f fuera una función, significaría la función en sí misma, concedida, pero este no es el caso aquí. Lo que realmente significa es esto:

val b = () => f()

En otras palabras, f _ es un cierre sobre una llamada de método. Y los cierres se implementan a través de funciones.

Finalmente, ¿por qué las listas de parámetros vacías son opcionales en Scala? Porque mientras Scala permite declaraciones como def f = 5 , Java no lo hace. Todos los métodos en Java requieren al menos una lista de parámetros vacía. Y hay muchos métodos de este tipo que, en el estilo de Scala, no tendrían ningún parámetro (por ejemplo, length y size ). Por lo tanto, para que el código se vea más uniforme con respecto a la lista de parámetros vacía, Scala los hace opcionales.


En tu ejemplo

def f(): Int = { counter = counter + 1; counter }

Es definir un método, no una función. Los métodos AFAIK se promueven a funciones automáticamente en Scala según el contexto. Para definir una función puedes escribir

val f = () => { counter = counter + 1; counter }

Y creo que obtendrás lo que quieres.


La causa por defecto, cuando escribes:

val b = f

es evaluar la función y asignar el resultado a b , como habrás notado. Puede usar el _ , o puede especificar explícitamente el tipo de b :

// These all have the same effect val b = f _ val b: () => Int = f val b: Function0[Int] = f


Su suposición es correcta: Scala tiene semántica dependiente del tipo con respecto a la evaluación de expresiones.

Al igual que Ruby, siempre evalúa thunks incluso sin paréntesis. (Esto podría tener ventajas para fines de interacción, ya que puede cambiar operaciones puras y posiblemente impuras sin tener que cambiar la sintaxis).

Pero como Scala tiene un poderoso sistema de tipo estático, puede romper la regla anterior y evitar que el programador aplique explícitamente parcialmente las funciones en los casos en que el resultado de una evaluación no tendría sentido desde una perspectiva de tipo.

Tenga en cuenta que la evaluación dependiente del tipo puede incluso simular call-by-name

Ahora bien, ¿el comportamiento de evaluación dependiente del tipo es bueno o malo? ... Bueno, ciertamente puede llevar a casos confusos como el suyo y ya no se siente tan puro . Pero en la mayoría de los casos, simplemente funciona según lo previsto por el programador (haciendo que el código sea más conciso). Entonces, digamos, está bien .