2012-04-30 15 views
6

que estaba bajo la impresión de que estefunciones al curry imperiosa de Scala

// short syntax 
def foo(bar: Bar)(baz: Baz): Quux 

era el azúcar sintaxis de este

// long syntax 
def foo(bar: Bar): (Baz) => Quux 

Pero me parece que no puede mezclar los dos en lo que respecta a la herencia. El árbol completo tiene que definirse en la sintaxis corta o en la sintaxis larga; nunca ambos.

Por ejemplo:

case class Context 
case class Work 

trait ContextualWorker { 
    def workWithContext(ctxt: Context)(work: Work): Traversable[Work] 
} 

class ShortConcreteWorker extends ContextualWorker { 
    override def workWithContext(ctxt: Context)(work: Work) = Nil 
} 

class LongConcreteWorker extends ContextualWorker { 
    // error on next line: method workWithContext overrides nothing <------------- 
override def workWithContext(ctxt: Context): (Work) => Traversable[Work] = { 
    val setupCode = 1 
    { work => Nil } 
    } 
} 

Si cambio el rasgo de usar la sintaxis de largo, entonces ShortConcreteWorker no compila.

¿Hay alguna razón por la cual estos no sean intercambiables/heredables? ¿Cómo lo has conseguido?

En este momento el enfoque más flexible parece ser definir el árbol en la sintaxis de largo, tal vez de delegar a una clase de implementación en ShortConcreteWorker así:

case class Context 
case class Work 

trait ContextualWorker { 
    def workWithContext(ctxt: Context): (Work) => Traversable[Work] 
} 

class ShortConcreteWorker extends ContextualWorker { 
    override def workWithContext(ctxt: Context) = workWithContextImpl(ctxt)_ 
    private def workWithContextImpl(ctxt: Context)(work: Work) = Nil 
} 

class LongConcreteWorker extends ContextualWorker { 
    override def workWithContext(ctxt: Context): (Work) => Traversable[Work] = { 
    val setupCode = 1 
    { work => Nil } 
    } 
} 
+0

Supongo que está basando su suposición en [esta página en el sitio de Scala] (http://www.scala-lang.org/node/135). Desafortunadamente no es del todo exacto, consulte la sección 3.3.1 de la especificación de Scala. – rxg

Respuesta

5

Los dos métodos descritos simplemente tienen diferentes firmas . El REPL confirma:

scala> def foo1(a: Int)(b: Int): Int = a + b 
foo1: (a: Int)(b: Int)Int 

scala> def foo2(a: Int): (Int => Int) = (b: Int) => a + b 
foo2: (a: Int)Int => Int 

La primera es una función que requiere dos argumentos, dados en la lista de argumentos separados, y devuelve un Int. La segunda es una función que toma un argumento y devuelve una función de Int a Int. Si bien estas dos cosas son conceptualmente similares, son, de hecho, construcciones diferentes, y Scala las trata como tales.

Esto no se limita a las funciones con múltiples listas de argumentos. Funciona de la misma manera aquí:

scala> def foo3(a: Int): Int = a + 1 
foo3: (a: Int)Int 

scala> def foo4: (Int => Int) = (a: Int) => a + 1 
foo4: Int => Int 

Tenga en cuenta que existen diferentes ramificaciones para el uso también. Con foo2, porque solo acepta un argumento, podemos llamarlo con un solo argumento. Sin embargo, foo1 requiere dos argumentos, por lo que no podemos simplemente llamarlo con uno. Usted puede, sin embargo, use la sintaxis _ para convertirla en una función invocable.

foo2(2) // Int => Int = <function1> 
foo1(2) // error: missing arguments for method foo1 
foo1(2) _ // Int => Int = <function1> 

Así que para responder a su pregunta directamente: La razón por la que no son intercambiables es porque no son lo mismo. Si fueran iguales, podríamos llamarlos de la misma manera. Si pudieras cambiar la firma en la extensión, ¿cómo sabría Scala qué sintaxis de llamada permitir? La forma de "evitarlo" es simplemente hacer que las firmas sean consistentes.

+0

Podría ser útil agregar que si tenemos, por ejemplo, 'def g (f: Int => Int)' entonces podemos llamarlo como 'g (foo1 (3))' o 'g (foo2 (3)) '. Entonces, bajo ciertas circunstancias, los dos estilos pueden ser indistinguibles. – rxg

Cuestiones relacionadas