2011-10-07 22 views
6

En el siguiente ejemplo de código, no entiendo por qué la función fun se puede pasar como argumento al método addAction. El método fun es del tipo Unit, mientras que el método addAction espera una función de tipo () => Unit.Métodos y métodos del método Scala como parámetros

Si fun es de tipo () => Unit, entonces ¿por qué se queja el compilador que fun es de tipo Unit, cuando intento agregar fun a la lista de acciones: actions = fun :: actions?

package myscala 

object MyScala { 

    def fun() { println("fun1 executed.") } 

    def addAction(a:() => Unit) { 
    actions = a :: actions 
    } 

    var actions: List[() => Unit] = List() 

    def main(args: Array[String]) { 
    // the following line would produce a compiler error (found: Unit, required:() => Unit), it's OK 
    // actions = fun :: actions 
    actions = (() => fun) :: actions // OK 
    // I would expect the same compiler error here (found: Unit, required:() => Unit), but it's OK why? 
    addAction(fun) 
    actions.foreach(_()) // prints twice "fun1 executed" 
    } 
} 

Respuesta

8

tomar esto como un ejemplo de introducción:

def fun() { println("fun1 executed.") } 

val a1 = fun 
val a2:() => Unit = fun 

Ambas líneas compilan y (gracias a la inferencia de tipos) se ven equivalente. Sin embargo, a1 es del tipo Unit, mientras que a2 es del tipo () => Unit ... ¿Cómo es esto posible?

Dado que usted no está proporcionando explícitamente tipo de a1, compiladores interpreta como un método funfun llamado de tipo Unit, por lo tanto, el tipo de a1 es el mismo que el tipo de fun. También significa que esta línea imprimirá fun1 ejecutada.

Sin embargo, a2 ha declarado explícitamente el tipo de () => Unit. El compilador lo ayuda aquí y entiende que dado que el contexto requiere una función de tipo () => Unit y proporcionó un método que coincida con este tipo, no debería llamar a ese método, ¡sino tratarlo como una función de primera clase!

No está condenado a especificar el tipo de a1 explícitamente. Diciendo:

val a1 = fun _ 

¿Ahora comprende dónde está su problema?

+0

Sí, lo hago. Esto ahora me parece obvio (no fue para nada en ese momento). Cuando el compilador es capaz de inferir que se espera un tipo de función, simplemente puedo escribir 'fun'; de lo contrario, tengo que explicitar que estoy aprobando una función. ¡Gracias por las claras respuestas para todos ustedes! – Manu

+0

@Manu: Considera aceptar una respuesta que consideres que es la mejor (no necesariamente esta) –

5

tiene que escribir fun _ en el primer caso para evitar una llamada al método y la realización de eta-expansión en su lugar.

Esto funcionará:

actions = (fun _) :: actions 

Si no lo hace, entonces se evalúa fun.

Para obtener más información, consulte la Sección 6.7 (Valores del método) del Scala Language Reference.

En cuanto a por qué fun no se evalúa en el segundo caso, es porque la inferencia de tipo puede concluir claramente que addAction espera una función. Por cierto, el tipo de fun es técnicamente ()Unit, no Unit, es decir, un tipo de método y no un tipo de valor. Consulte la Sección 3.3.1 en el reference para obtener más información.

+3

prefiero recomendar una lectura más clara: [Programación en Scala Capítulo 9] (http://www.artima.com/pins1ed/control-abstraction.html) – Jamil

3

Existe una diferencia entre los métodos y las funciones. En su caso, actions es una lista de funciones.Cuando el compilador sabe que se requiere una función (como en el caso de addAction) puede convertir automáticamente un método fun en una función. Ahora :: es también un método, por lo tanto, el compilador también sabe que toma funciones como parámetros. Pero el problema es el azúcar sintáctico del operador asociativo de la derecha ::. Si tuviera que llamarlo como un método: actions.::(fun) se compilará (aunque no puedo probarlo en este momento). Al escribir fun :: actions, el compilador cree que fun es una expresión y, por lo tanto, la evalúa y, dado que "devuelve" un Unit, obtiene el error de compilación.

EDITAR

ya que ahora tengo la posibilidad de probar mi hipótesis (que estaba mal) aquí están sus opciones:

// Usual syntax 
actions.::[() => Unit](fun) 
actions.::(fun:() => Unit) 
actions.::(fun _) 
// Operator syntax 
(fun:() => Unit) :: actions 
(fun _) :: actions 
+0

'actions.: :(fun)' evalúa 'List [Any] = List (())'. Todavía está evaluando la función sin embargo. ¿Se pregunta por qué la Lista [() => Unidad] se convierte en Lista [Cualquiera]? co-varianza? – Jamil

+2

Sí, 'List [Any]' es el tipo más preciso para una lista que puede contener 'Unit's y'() => Unit's. La 'Unidad', vista como'() ', es lo que devuelve la evaluación de 'diversión'. – Philippe

+0

He editado mi respuesta. – agilesteel

Cuestiones relacionadas