Возврат частично примененной функции в Scala - PullRequest
2 голосов
/ 23 апреля 2019

Имея функцию f, определенную как:

def f(i1: Int, i2: Int)(i3: Int) = i1 + i2 + i3

С помощью _ можно определить частично примененную функцию следующим образом:

val f12 = f(1, 2) _  // f12: Int => Int = <function>
f12(3)  // res0: Int = 6

Теперь, когда я возвращаю частично примененную функцию из функции, мне не нужно использовать _:

def f23(f: (Int, Int) => Int => Int) = f(2, 3)  // f23: (f: (Int, Int) => Int => Int)Int => Int
val f23f = f23(f)  // f23f: Int => Int = <function>
f23f(4)  // res1: Int = 9

Если я добавлю _ в определение f23, я получу ошибку:

def f23(f: (Int, Int) => Int => Int) = f(2, 3) _
Error:(6, 49) _ must follow method; cannot follow Int => Int
def f23(f: (Int, Int) => Int => Int) = f(2, 3) _

В чем причина этого несоответствия?

Ответы [ 2 ]

3 голосов
/ 24 апреля 2019

f - это метод, а не функция. О некоторых различиях вы можете прочитать здесь .

f12 - это функция, полученная из f через eta-расширение. Это не частичная функция. A PartialFunction - это функция, определенная в ограниченной области входных значений. Если, скажем, f12 был определен только для Int значений, например, менее 500, и не определен для входных значений, превышающих 500, то это будет частичная функция.

def f23(f: (Int, Int) => Int => Int) = f(2, 3) _

Это невозможно, потому что f, как определено здесь, является функцией, которая принимает значения 2 Int и возвращает функцию, которая принимает Int и возвращает Int. В этой ситуации за что стоит стоять подчеркивание? f(2,3) - это полный вызов, который возвращает функцию Int=>Int. Это немного похоже на написание 5 + 7 _. Непонятно, что заменяет _.

С другой стороны, вы можете сделать это: ... = f(2,3)(_). Затем становится ясно, что возвращаемая функция вызывается с отсутствующим параметром, т.е. _. Что то же самое, что и ... = f(2,3).

1 голос
/ 23 апреля 2019

Это сделано в Scala для предотвращения путаницы разработчиков. Если вы сообщите компилятору тип f12 явно, он будет работать так, как вы ожидаете:

`val f12:Int=>Int = f(1, 2)`

Это объясняет создатель языка (Мартин Одерский):

Почему завершающий знак подчеркивания?

Синтаксис Scala для частично примененных функций подчеркивает разницу в компромиссных решениях Scala и классических функциональных языков, таких как Haskell или ML. В этих языках частично применяемые функции считаются нормальным случаем. Кроме того, эти языки имеют довольно строгую систему статических типов, которая обычно выделяет каждую ошибку с частичными приложениями, которые вы можете сделать. Scala имеет гораздо более тесную связь с императивными языками, такими как Java, где метод, который не применяется ко всем его аргументам, считается ошибкой. Кроме того, объектно-ориентированная традиция подтипирования и универсального корневого типа принимает некоторые программы, которые считались бы ошибочными в классических функциональных языках.

Например, допустим, вы приняли метод drop (n: Int) в List для tail (), и поэтому вы забыли, что вам нужно передать число для отбрасывания. Вы можете написать «println (drop)». Если бы Scala приняла классическую функциональную традицию, что частично применяемые функции везде в порядке, этот код проверял бы тип. Тем не менее, вы можете быть удивлены, узнав, что вывод, напечатанный этим оператором println, всегда будет! То, что случилось бы, - то, что падение выражения рассматривалось бы как функциональный объект. Поскольку println принимает объекты любого типа, это скомпилировало бы OK, но это дало бы неожиданный результат.

Чтобы избежать подобных ситуаций, Scala обычно требует, чтобы вы указывали аргументы функций, которые явно не указаны, даже если указание так же просто, как `_ '. Scala позволяет отключать даже _ только тогда, когда ожидается тип функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...