Previous

Next

Tour of Scala

Multiple Parameter Lists (Currying)

Methods may define multiple parameter lists. When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing parameter lists as its arguments. This is formally known as currying.

Here is an example, defined in Traversable trait from Scala collections:

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

foldLeft applies a binary operator op to an initial value z and all elements of this traversable, going left to right. Shown below is an example of its usage.

Starting with an initial value of 0, foldLeft here applies the function (m, n) => m + n to each element in the List and the previous accumulated value.

In [1]:
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
print(res) // 55
55
Out[1]:
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
res: Int = 55

Multiple parameter lists have a more verbose invocation syntax; and hence should be used sparingly. Suggested use cases include:

Single functional parameter

In case of a single functional parameter, like op in the case of foldLeft above, multiple parameter lists allow a concise syntax to pass an anonymous function to the method. Without multiple parameter lists, the code would look like this:

In [2]:
numbers.foldLeft(0, {(m: Int, n: Int) => m + n})
cmd1.sc:1: missing argument list for method foldLeft in trait LinearSeqOptimized
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `foldLeft _` or `foldLeft(_)(_)` instead of `foldLeft`.
val res1 = numbers.foldLeft(0, {(m: Int, n: Int) => m + n})
                           ^Compilation Failed
Compilation Failed

Note that the use of multiple parameter lists here also allows us to take advantage of Scala type inference to make the code more concise as shown below; which would not be possible in a non-curried definition.

In [2]:
numbers.foldLeft(0)(_ + _)
Out[2]:
res1: Int = 55

Above statement numbers.foldLeft(0)(_ + _) allows us to fix the parameter z and pass around a partial function and reuse it as shown below:

In [3]:
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val numberFunc = numbers.foldLeft(List[Int]())_

val squares = numberFunc((xs, x) => xs:+ x*x)
print(squares.toString()) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

val cubes = numberFunc((xs, x) => xs:+ x*x*x)
print(cubes.toString())  // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
Out[3]:
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
numberFunc: (List[Int], Int) => List[Int] => List[Int] = ammonite.$sess.cmd2$Helper$$Lambda$2487/[email protected]
squares: List[Int] = List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
cubes: List[Int] = List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)

Finally, foldLeft and foldRight can be used in any of the following terms,

In [4]:
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

numbers.foldLeft(0)((sum, item) => sum + item) // Generic Form
numbers.foldRight(0)((sum, item) => sum + item) // Generic Form

numbers.foldLeft(0)(_+_) // Curried Form
numbers.foldRight(0)(_+_) // Curried Form

(0 /: numbers)(_+_) // Used in place of foldLeft
(numbers :\ 0)(_+_) // Used in place of foldRight
Out[4]:
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
res3_1: Int = 55
res3_2: Int = 55
res3_3: Int = 55
res3_4: Int = 55
res3_5: Int = 55
res3_6: Int = 55

Implicit parameters

To specify certain parameters in a parameter list as implicit, multiple parameter lists should be used. An example of this is:

def execute(arg: Int)(implicit ec: ExecutionContext) = ???

Previous

Next

Tour of Scala