Полезность (как в практических приложениях) 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)

в любом месте вашего кода или что это в любом случае особенно важно. Просто вы легко получаете красивое выражение.

(Ну, и есть некоторые преимущества в отношении вывода типа...)