Usefulness (as in practical applications) of Curry

2019-01-07 05:06发布

问题:

I'm trying to understand the advantages of currying over partial applications in Scala. Please consider the following code:

  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)

So, if I can calculate partially applied function that easy, what are the advantages of currying?

回答1:

Currying is mostly used if the second parameter section is a function or a by name parameter. This has two advantages. First, the function argument can then look like a code block enclosed in braces. E.g.

using(new File(name)) { f =>
  ...
}

This reads better than the uncurried alternative:

using(new File(name), f => {
  ...
})

Second, and more importantly, type inference can usually figure out the function's parameter type, so it does not have to be given at the call site. For instance, if I define a max function over lists like this:

def max[T](xs: List[T])(compare: (T, T) => Boolean)

I can call it like this:

max(List(1, -3, 43, 0)) ((x, y) => x < y)

or even shorter:

max(List(1, -3, 43, 0)) (_ < _)

If I defined max as an uncurried function, this would not work, I'd have to call it like this:

max(List(1, -3, 43, 0), (x: Int, y: Int) => x < y)

If the last parameter is not a function or by-name parameter, I would not advise currying. Scala's _ notatation is amost as lightweight, more flexible, and IMO clearer.



回答2:

I think it becomes clearer if you invert your curried example:

def sum4(a: Int, b: Int)(f: Int => Int): Int = f(a) + f(b)

val d = sum4(2, 2) { x =>
  x * x
}

It is more of an optical effect but you don’t need to use any parentheses around the whole expression. Of course you can achieve the same result using partial application or by creating a helper method to invert the arguments, sure. The point is, that you don’t have to do all of this if you start with a curried method in the first place. In that sense currying is more of an API and syntax sugar thing. It is not expected that you use

val partial_sum4 = sum4(2, 2)

anywhere in your code or that this is in any way especially meaningful to do. It is just that you get a nicely looking expression easily.

(Well, and there are some advantages with respect to type inference…)