What's the purpose of Currying given other alternatives to return a function in Scala?

Viewed 287

I'm currently doing a Scala course and recently I was introduced to different techniques of returning functions.

For example, given this function and method:

val simpleAddFunction = (x: Int, y: Int) => x + y
def simpleAddMethod(x: Int, y: Int) = x + y

I can return another function just doing this:

val add7_v1 = (x: Int) => simpleAddFunction(x, 7)
val add7_v2 = simpleAddFunction(_: Int, 7)
val add7_v3 = (x: Int) => simpleAddMethod(x, 7)
val add7_v4 = simpleAddMethod(_: Int, 7)

All the values add7_x accomplish the same thing, so, whats the purpose of Currying then? Why I have to write def simpleCurryMethod(x: Int)(y: Int) = x + y if all of the above functions do a similar functionality?

That's it! I'm a newbie in functional programming and I don't know many use cases of Currying apart from saving time by reducing the use of parameters repeatedly. So, if someone could explain me the advantages of currying over the previous examples or in Currying in general I would be very grateful.

That's it, have a nice day!

1 Answers

In Scala 2 there are only four pragmatic reasons for currying METHODS (as far as I can recall, if someone has another valid use case then please let me know).

  1. (and probably the principal reason to use it) to drive type inference.

For example, when you want to accept a function or another kind of generic value whose generic type should be inferred from some plain data. For example:

def applyTwice[A](a: A)(f: A => A): A = f(f(a))

applyTwice(10)(_ + 1) // Here the compiler is able to infer that f is Int => Int

In the above example, if I wouldn't have curried the function then I would need to have done something like: applyTwice(10, (x: Int) => x + 1) to call the function.
Which is redundant and looks worse (IMHO).

Note: In Scala 3 type inference is improved thus this reason is not longer valid there.

  1. (and probably the main reason now in Scala 3) for the UX of callers.

For example, if you expect an argument to be a function or a block it is usually better as a single argument in its own (and last) parameter list so it looks nice in usage. For example:

def iterN(n: Int)(body: => Unit): Unit =
  if (n > 0) {
    body
    iterN(n - 1)(body)
  }

iterN(3) {
  println("Hello")
  // more code
  println("World")
}

Again, if I wouldn't have curried the previous method the usage would have been like this:

iterN(3, {
  println("Hello")
  // more code
  println("World")
})

Which doesn't look that nice :)

  1. (in my experience weird but valid) when you know that majority of users will call it partially to return a function.

Because val baz = foo(bar) _ looks better than val baz = foo(bar, _) and with the first one, you sometimes don't the the underscore like: data.map(foo(bar))

Note: Disclaimer, I personally think that if this is the case, is better to just return a function right away instead of currying.


Edit

Thanks to @jwvh for pointing out this fourth use case.

  1. (useful when using path-dependant types) when you need to refer to previous parameters. For example:
trait Foo {
  type I

  def bar(i: I): Baz
}

def run(foo: Foo)(i: foo.I): Baz =
  foo.bar(i)
Related