2010-03-02 10 views
32

Esto produce una función anónima, como era de esperar (f es una función con tres argumentos):En Scala, ¿por qué no puedo aplicar parcialmente una función sin especificar explícitamente sus tipos de argumentos?

f(_, _, _) 

Lo que no entiendo es por qué esto no se compila, en lugar de dar un "tipo parámetro que falta "error:

f(_, _, 27) 

En su lugar, necesito especificar los tipos de los caracteres de subrayado explícitamente. ¿No debería Scala ser capaz de inferirlos dado que sabe cuáles son los tipos de parámetros de la función f?

+3

¿Puedes dar un ejemplo de código real de lo que intentas hacer? Usted dice que esto "no compila" - * ¿en qué contexto? * –

+0

Gracias por su respuesta. En realidad, no estoy usando este tipo de fragmento de código en la práctica, pero me encontré con el problema simplemente mientras jugaba con la sintaxis del marcador de posición de Scala. La última parte de tu respuesta describe bastante bien lo que estaba tratando de hacer. –

+3

Parece una limitación arbitraria de la implementación del tipo inferencer. La verdad se encuentra en algún lugar aquí: http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/compiler/scala/tools/nsc/typechecker/Infer.scala – retronym

Respuesta

17

Referencias a continuación son a la Scala Language Specification

Considere el siguiente método:

def foo(a: Int, b: Int) = 0 

Eta Expansion puede convertir esto a un valor de tipo (Int, Int) => Int. Esta expansión se invoca si: ((§6.7 Método del valor))

val f = foo _ 

b) se omite la lista de argumentos

a) _ se utiliza en lugar de la lista de argumentos, y espera que el tipo de expresión es un tipo de función (§6.25.2):

val f: (Int, Int) => Int = foo 

c) cada uno de los argumentos es _ (a special case del 'marcador de posición sintaxis para funciones anónimas' (§ 6.23))

val f = foo(_, _) 

La expresión, foo(_, 1) no califica para Eta Expansion; simplemente se expande a (a) => foo(a, 1) (§6.23). La inferencia de tipo regular no intenta averiguar que a: Int.

+9

"La inferencia de tipo regular no intenta descubrir que a: Int.". Y puedo preguntar, ¿Por qué? –

+2

Escriba la inferencia de los parámetros de la función anon, p. Ej. el 'a' en' (a) => foo (a, 1) ', solo se basa en el tipo esperado, no en los usos del parámetro en el cuerpo de la función. – retronym

+1

Supongo que lo que estás preguntando es si las reglas podrían cambiarse para que 'foo (_, 1)' se expanda a '(a: Int) => foo (a, 1)', tal vez suponiendo que hay es solo una sobrecarga de 'foo' que coincide con la aridad y los otros argumentos. Si bien suena bastante fácil, creo que añadiría una complejidad adicional significativa a las especificaciones y la implementación. – retronym

8

Si usted está pensando en una aplicación parcial, pensé que esto sólo era posible con múltiples listas de parámetros (mientras que sólo tiene una):

def plus(x: Int)(y: Int) = x + y //x and y in different parameter lists 

val plus10 = plus(10) _ //_ indicates partial application 

println(plus10(2)) //prints 12 

Su ejemplo es interesante, ya que me estaba completamente inconsciente de la sintaxis que describes y parece que puede tener una aplicación parcial con una única lista de parámetros:

scala> def plus2(x: Int, y: Int) = x + y 
plus2: (x: Int,y: Int)Int 

scala> val anon = plus2(_,_) 
anon: (Int, Int) => Int = <function2> 

scala> anon(3, 4) 
res1: Int = 7 

lo que el compilador puede deducir claramente el tipo Int!

scala> val anon2 = plus2(20,_) 
<console>:5: error: missing parameter type for expanded function ((x$1) => plus2(20, x$1)) 
     val anon2 = plus2(20,_) 
          ^

Hmmm, extraño! No parece poder hacer una aplicación parcial con una sola lista de parámetros. ¡Pero luego si declaro el tipo del segundo parámetro, I puede tiene aplicación parcial!

scala> val anon2 = plus2(20,_: Int) 
anon2: (Int) => Int = <function1> 

scala> anon2(24) 
res2: Int = 44 

EDITAR - Lo único que puedo observar es que parece que los siguientes dos mantecas son equivalentes, en cuyo caso es un poco más obvio que esto no es una "aplicación parcial", sino más bien como un " función de puntero"

val anon1 = plus2(_,_) 
val anon2 = plus2 _ 
+2

'val f = foo (_, _)' se expande a 'val f = (a, b) => foo (a, b)', no es exactamente lo mismo que aplicar parcialmente una función curried . – retronym

+0

Pero * parece * tener el mismo efecto, debes admitirlo. Y si el compilador puede inferir tipos con 2 marcadores de posición '' ', ¿por qué tiene problemas cuando solo hay uno? –

+1

Para responder a esta pregunta, se puede depurar a través Infer.scala en el compilador :) Estoy de acuerdo, parece que una limitación innecesaria, pero la inferencia de tipos de Scala se omite intencionalmente de la especificación para permitir dicho margen de maniobra. – retronym

-3

Siento que este es uno de esos casos fronterizos derivados de toda la conversión de código, ya que esto implica la creación de una función anónima que dirige la llamada al método original. El tipo es para el argumento de la función anónima externa. En realidad, se puede especificar cualquier es decir subtipo

val f = foo(_: Nothing, 1) 

incluso esto sería compilar

+3

Sí, pero no puede llamar a esa función, porque el argumento debe ser de ese subtipo. De ahí mi voto negativo. – Blaisorblade

1

Creo que se debe a la sobrecarga hace que sea imposible que el compilador para inferir los tipos:

scala> object Ashkan { def f(a:Int,b:Int) = a; def f(a:Int,b:String) = b; } 
defined object Ashkan 

scala> Ashkan.f(1,2) 
res45: Int = 1 

scala> Ashkan.f(1,"Ashkan") 
res46: String = Ashkan 

scala> val x= Ashkan.f _ 
<console>:11: error: ambiguous reference to overloaded definition, 
both method f in object Ashkan of type (a: Int, b: String)String 
and method f in object Ashkan of type (a: Int, b: Int)Int 
match expected type ? 
     val x= Ashkan.f _ 
        ^

scala> val x= Ashkan.f(_,_) 
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => Ashkan.f(x$1, x$2)) 
     val x= Ashkan.f(_,_) 
        ^
<console>:11: error: missing parameter type for expanded function ((x$1: <error>, x$2) => Ashkan.f(x$1, x$2)) 
     val x= Ashkan.f(_,_) 
         ^

scala> val x= Ashkan.f(_,"Akbar") 
<console>:11: error: missing parameter type for expanded function ((x$1) => Ashkan.f(x$1, "Akbar")) 
     val x= Ashkan.f(_,"Akbar") 
        ^

scala> val x= Ashkan.f(1,_) 
<console>:11: error: missing parameter type for expanded function ((x$1) => Ashkan.f(1, x$1)) 
     val x= Ashkan.f(1,_) 
         ^

scala> val x= Ashkan.f(1,_:String) 
x: String => String = <function1> 
Cuestiones relacionadas