Pregunta En Scala, ¿por qué no puedo aplicar parcialmente una función sin especificar explícitamente sus tipos de argumentos?


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?


32
2018-03-02 12:24


origen


Respuestas:


Las referencias a continuación son para el Especificación del lenguaje Scala

Considera el siguiente método:

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

Eta Expansion puede convertir esto en un valor de tipo (Int, Int) => Int. Esta expansión se invoca si:

un) _ se usa en lugar de la lista de argumentos (Valor del método (§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 expresion, 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.


17
2018-03-06 20:51



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

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, poder 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 _

8
2018-03-02 12:44



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>

1
2017-09-22 12:34



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


-3
2018-03-07 20:49