В Scala почему я не могу частично применить функцию без явного указания ее типов аргументов?
Это создает анонимную функцию, как и следовало ожидать (f - это функция с тремя аргументами):
f(_, _, _)
Я не понимаю, почему это не компилируется, вместо этого возникает ошибка "Отсутствующий тип параметра":
f(_, _, 27)
Вместо этого мне нужно явно указать типы подчеркиваний. Не следует ли Scala выводить их, учитывая, что он знает, что представляют собой типы параметров функции f?
Ответы
Ответ 1
Ниже приведены ссылки Scala Спецификация языка
Рассмотрим следующий метод:
def foo(a: Int, b: Int) = 0
Eta Expansion может преобразовать это значение в значение типа (Int, Int) => Int
. Это расширение вызывается, если:
a) _
используется вместо списка аргументов (значение метода (§6.7))
val f = foo _
b) список аргументов опущен, а ожидаемый тип выражения - это тип функции (§6.25.2):
val f: (Int, Int) => Int = foo
c) каждый из аргументов _
(a специальный случай синтаксиса-заполнителя для анонимных функций (§6.23))
val f = foo(_, _)
Выражение foo(_, 1)
не относится к Eta Expansion; он просто расширяется до (a) => foo(a, 1)
(§6.23). Регулярный вывод типа не пытается понять, что a: Int
.
Ответ 2
Если вы думаете о частичном приложении, я подумал, что это возможно только с списками параметров (тогда как у вас их есть только один):
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
Ваш пример интересен, хотя, поскольку я полностью не знал о синтаксисе, который вы описываете, и кажется, что вы можете иметь частичное приложение с единственным списком параметров:
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
Таким образом, компилятор может четко указать тип 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,_)
^
Хммм, странно! Кажется, я не могу делать частичное приложение с единственным списком параметров. Но если я объявляю тип второго параметра, я могу иметь частичное приложение!
scala> val anon2 = plus2(20,_: Int)
anon2: (Int) => Int = <function1>
scala> anon2(24)
res2: Int = 44
EDIT - я бы заметил, что следующие два сокращения эквивалентны, и в этом случае немного более очевидно, что это не "частичное приложение", а скорее как "указатель функции"
val anon1 = plus2(_,_)
val anon2 = plus2 _
Ответ 3
Я думаю, это связано с тем, что перегрузка делает невозможным компилятор для вывода типов:
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>
Ответ 4
Я чувствую, что это один из тех пограничных случаев, которые возникают из-за всего преобразования кода, поскольку это связано с созданием анонимной функции, которая направляет вызов исходному методу. Тип - для аргумента внешней анонимной функции. Фактически вы можете указать любой подтип i.e
val f = foo(_: Nothing, 1)
даже это скомпилировало бы