software - En Scala, ¿por qué no puedo aplicar parcialmente una función sin especificar explícitamente sus tipos de argumentos?
scala vs java (4)
Creo que es porque la sobrecarga hace que sea imposible para el compilador 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>
Esto produce una función anónima, como era de esperar (f es una función con tres argumentos):
f(_, _, _)
Lo que no entiendo es por qué esto no se compila, sino que aparece un error de "tipo de parámetro faltante":
f(_, _, 27)
En cambio, necesito especificar los tipos de guiones bajos 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?
Las referencias a continuación son para la especificación del lenguaje Scala
Considera 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:
a) _
se usa en lugar de la lista de argumentos (Method Value (§6.7))
val f = foo _
b) se omite la lista de argumentos, y el tipo de expresión esperado es un tipo de función (§6.25.2):
val f: (Int, Int) => Int = foo
c) cada uno de los argumentos es _
(un caso especial de la ''Sintaxis de marcador de posición 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 descubrir que a: Int
.
Si está pensando en la aplicación parcial, pensé que esto solo era posible con listas de parámetros múltiples (mientras que usted solo 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
Sin embargo, su ejemplo es interesante ya que desconocía por completo la sintaxis que describe y parece que puede tener una aplicación parcial con una sola 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
Entonces el compilador puede inferir 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 si declaro el tipo del segundo parámetro, puedo tener una aplicación parcial!
scala> val anon2 = plus2(20,_: Int)
anon2: (Int) => Int = <function1>
scala> anon2(24)
res2: Int = 44
EDITAR - una cosa que observaría es que parece que las siguientes dos shortenings son equivalentes, en cuyo caso es un poco más obvio que esto no es una "aplicación parcial" sino más bien un "puntero de función"
val anon1 = plus2(_,_)
val anon2 = plus2 _
Siento que este es uno de esos casos límite que surgen de la conversión de todos los códigos, 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. De hecho, puede especificar cualquier subtipo, es decir,
val f = foo(_: Nothing, 1)
incluso esto compilaría