Полезность (как в практических приложениях) Currying v.s. Частичное применение в Scala
Я пытаюсь понять преимущества обработки некоторых приложений в Scala. Пожалуйста, рассмотрите следующий код:
def sum(f: Int => Int) = (a: Int, b: Int) => f(a) + f(b)
def sum2(f: Int => Int, a: Int, b: Int): Int = f(a) + f(b)
def sum3(f: Int => Int)(a: Int, b: Int): Int = f(a) + f(b)
val ho = sum({identity})
val partial = sum2({ identity }, _, _)
val currying = sum3({ identity })
val a = currying(2, 2)
val b = partial(2, 2)
val c = ho(2, 2)
Итак, если я могу легко вычислить частично прикладную функцию, каковы преимущества каррирования?
Ответы
Ответ 1
Currying в основном используется, если вторая секция параметров является функцией или параметром по имени. Это имеет два преимущества. Во-первых, аргумент функции может выглядеть как блок кода, заключенный в фигурные скобки. Например.
using(new File(name)) { f =>
...
}
Это лучше, чем альтернативный вариант:
using(new File(name), f => {
...
})
Во-вторых, и что более важно, вывод типа может обычно определять тип параметра функции, поэтому его не нужно указывать на сайте вызова.
Например, если я определяю функцию max
над списками, подобными этому:
def max[T](xs: List[T])(compare: (T, T) => Boolean)
Я могу назвать это следующим образом:
max(List(1, -3, 43, 0)) ((x, y) => x < y)
или даже короче:
max(List(1, -3, 43, 0)) (_ < _)
Если бы я определил max
как неокрашенную функцию, это не сработало бы, я бы назвал это следующим образом:
max(List(1, -3, 43, 0), (x: Int, y: Int) => x < y)
Если последний параметр не является параметром функции или имени, я бы не советовал карри. Scala _
Нотация является более легкой, более гибкой и более понятной IMO.
Ответ 2
Я думаю, что становится яснее, если вы инвертируете свой пример с карри:
def sum4(a: Int, b: Int)(f: Int => Int): Int = f(a) + f(b)
val d = sum4(2, 2) { x =>
x * x
}
Это скорее оптический эффект, но вам не нужно использовать любые круглые скобки вокруг всего выражения. Конечно, вы можете добиться того же результата с помощью частичного приложения или путем создания вспомогательного метода для инвертирования аргументов. Дело в том, что вам не нужно все это делать, если вы начинаете с метода карри в первую очередь. В этом смысле каррирование - это скорее API и синтаксический сахар. Не предполагается, что вы используете
val partial_sum4 = sum4(2, 2)
в любом месте вашего кода или что это в любом случае особенно важно. Просто вы легко получаете красивое выражение.
(Ну, и есть некоторые преимущества в отношении вывода типа...)