Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two ways of currying in Scala; what's the use-case for each?

I am having a discussion around Multiple Parameter Lists in the Scala Style Guide I maintain. I've come to realize that there are two ways of currying, and I'm wondering what the use cases are:

def add(a:Int)(b:Int) = {a + b} // Works add(5)(6) // Doesn't compile val f = add(5) // Works val f = add(5)_ f(10) // yields 15  def add2(a:Int) = { b:Int => a + b } // Works add2(5)(6) // Also works val f = add2(5) f(10) // Yields 15 // Doesn't compile val f = add2(5)_ 

The style guide incorrectly implies these are the same, when they are clearly not. The guide is trying to make a point about created curried functions, and, while the second form is not "by-the-book" currying, it's still very similar to the first form (though arguably easier to use because you don't need the _)

From those that use these forms, what's the consensus on when to use one form over the other?

like image 495
davetron5000 Avatar asked Feb 06 '11 17:02

davetron5000


1 Answers

Multiple Parameter List Methods

For Type Inference

Methods with multiple parameter sections can be used to assist local type inference, by using parameters in the first section to infer type arguments that will provide an expected type for an argument in the subsequent section. foldLeft in the standard library is the canonical example of this.

def foldLeft[B](z: B)(op: (B, A) => B): B  List("").foldLeft(0)(_ + _.length) 

If this were this written as:

def foldLeft[B](z: B, op: (B, A) => B): B 

One would have to provide more explicit types:

List("").foldLeft(0, (b: Int, a: String) => a + b.length) List("").foldLeft[Int](0, _ + _.length) 

For fluent API

Another use for multiple parameter section methods is to create an API that looks like a language construct. The caller can use braces instead of parentheses.

def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)  loop(2) {    println("hello!") } 

Application of N argument lists to method with M parameter sections, where N < M, can be converted to a function explicitly with a _, or implicitly, with an expected type of FunctionN[..]. This is a safety feature, see the change notes for Scala 2.0, in the Scala References, for an background.

Curried Functions

Curried functions (or simply, functions that return functions) more easily be applied to N argument lists.

val f = (a: Int) => (b: Int) => (c: Int) => a + b + c val g = f(1)(2) 

This minor convenience is sometimes worthwhile. Note that functions can't be type parametric though, so in some cases a method is required.

Your second example is a hybrid: a one parameter section method that returns a function.

Multi Stage Computation

Where else are curried functions useful? Here's a pattern that comes up all the time:

def v(t: Double, k: Double): Double = {    // expensive computation based only on t    val ft = f(t)     g(ft, k) }  v(1, 1); v(1, 2); 

How can we share the result f(t)? A common solution is to provide a vectorized version of v:

def v(t: Double, ks: Seq[Double]: Seq[Double] = {    val ft = f(t)    ks map {k => g(ft, k)} } 

Ugly! We've entangled unrelated concerns -- calculating g(f(t), k) and mapping over a sequence of ks.

val v = { (t: Double) =>    val ft = f(t)    (k: Double) => g(ft, k)        } val t = 1 val ks = Seq(1, 2) val vs = ks map (v(t)) 

We could also use a method that returns a function. In this case its a bit more readable:

def v(t:Double): Double => Double = {    val ft = f(t)    (k: Double) => g(ft, k)        } 

But if we try to do the same with a method with multiple parameter sections, we get stuck:

def v(t: Double)(k: Double): Double = {                 ^                 `-- Can't insert computation here! } 
like image 188
retronym Avatar answered Sep 21 '22 22:09

retronym