2010-05-29 14 views
26

Justo después de las 02:40 en ShadowofCatron 's Scala Tutorial 3 video, se señaló que los paréntesis a continuación del 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.¿Por qué Scala aplica los thunk automáticamente, a veces?

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 tener claro el problema, este programa Esquema (y el volcado de la consola que sigue) muestra lo que esperaba el programa Scala hacer.

(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 llegar a este sitio para preguntar acerca de esto, me encontré con this answer que me ha dicho cómo solucionar lo anterior Scala programa:

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

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

 

Todo eso me deja con la pregunta:

¿Por qué Scala veces se aplican automáticamente cuando thunks evaluación de las mismas?

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

¿Es esta una buena idea? ¿Los programadores de Scala aplican thunks en lugar de referirse a sus valores con mucha más frecuencia que hacer que los parens sean opcionales es mejor en general?


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

+3

+1 una pregunta excelentemente escrita. – Dario

Respuesta

14

El problema está aquí:?

Buh" dijo mi funcional de programación cerebro, ya que el valor de una función y el valor se evalúa como cuando aplicados son completamente diferentes cosas

.

Sí, pero no declaró ninguna función.

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

Has declarado 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 Method a través de la reflexión, que no es realmente lo mismo en absoluto.

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

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

val b =() => f() 

En otras palabras, es un cierre f _ más de una llamada al 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 aunque 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 que, en el estilo de Scala, no tienen ningún parámetro (por ejemplo, length y size). Entonces, para que el código se vea más uniforme con respecto a la lista de parámetros vacíos, Scala los hace opcionales.

9

Supongo que es correcto: Scala tiene una semántica dependiente del tipo con respecto a la evaluación de expresiones.

Al igual que Ruby, es siempre evalúa los thunks incluso sin paréntesis. (Esto puede tener ventajas para los propósitos de interacción ya que se puede cambiar operaciones puras y posiblemente impuros sin tener que cambiar la sintaxis.)

Pero desde Scala dispone de un potente sistema de tipo estático, puede romper la regla anterior y guardar la programador de aplicar 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 evalution tipo dependiente puede incluso simular call-by-name


Ahora es el comportamiento evalution dependiente del tipo que es bueno o malo? ... Bueno, ciertamente puede conducir a casos confusos como el tuyo y ya no siente que puro. Pero en la mayoría de los casos, solo funciona según lo previsto por el programador (por lo que el código es más conciso) - Así que digamos, es bien.

13

La causa por defecto, cuando escribe:

val b = f 

es la evaluación de la función y asignar el resultado a b, como habrán notado. Se puede utilizar 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 
12

En su ejemplo

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

es la definición de un método, no una función. Los métodos AFAIK se promocionan a funciones automáticamente en Scala según el contexto. Para definir una función, puede escribir

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

y creo que obtendrá lo que desea.

+0

Aha, eso lo explica! Entonces, lo que estás diciendo es que crear un valor de función anónimo * realmente crea un valor de función * (y luego se le da un nombre con 'val') mientras que lo que parece un atajo de definición (ej.' (Define (f args) cuerpo) 'para Scheme) hace algo equivalente-ish en Scala, pero algo completamente diferente en el nivel de JVM. Gracias. Esto es exactamente lo que quería saber. – Anonymoose

+2

@Anonymouse Eso no es realmente correcto. El valor devuelto por el método 'f' en su declaración es' Int', tal como lo especificó. Si declaró 'def f():() => Int =() => {contador = contador + 1; contador} ', entonces tendría un método para devolver una función. Aún así, '{contador = contador + 1; contador} 'no es _una función anónima. Es un bloque que evalúa a cualquier tipo de 'contador '. Entonces esto no es una cosa 'val' vs' def' - solo hay que tener en cuenta que los métodos no son funciones. –

Cuestiones relacionadas