Tour of Scala
Multiple Parameter ListsMethods may have multiple parameter lists.
ExampleHere is an example, as defined on the Iterable
trait in Scalaâs collections API:
trait Iterable[A] {
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
}
trait Iterable[A]:
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
foldLeft
applies a two-parameter function op
to an initial value z
and all elements of this collection, 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.
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
println(res) // 55
Use cases
Suggested use cases for multiple parameter lists include:
Drive type inferenceIt so happens that in Scala, type inference proceeds one parameter list at a time. Say you have the following method:
def foldLeft1[A, B](as: List[A], b0: B, op: (B, A) => B) = ???
Then youâd like to call it in the following way, but will find that it doesnât compile:
def notPossible = foldLeft1(numbers, 0, _ + _)
you will have to call it like one of the below ways:
def firstWay = foldLeft1[Int, Int](numbers, 0, _ + _)
def secondWay = foldLeft1(numbers, 0, (a: Int, b: Int) => a + b)
Thatâs because Scala wonât be able to infer the type of the function _ + _
, as itâs still inferring A
and B
. By moving the parameter op
to its own parameter list, A
and B
are inferred in the first parameter list. These inferred types will then be available to the second parameter list and _ + _
will match the inferred type (Int, Int) => Int
def foldLeft2[A, B](as: List[A], b0: B)(op: (B, A) => B) = ???
def possible = foldLeft2(numbers, 0)(_ + _)
This definition doesnât need any type hints and can infer all of its type parameters.
Implicit parametersTo specify only certain parameters as implicit
, they must be placed in their own implicit
parameter list.
An example of this is:
def execute(arg: Int)(implicit ec: scala.concurrent.ExecutionContext) = ???
def execute(arg: Int)(using ec: scala.concurrent.ExecutionContext) = ???
Partial application
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 partial application.
For example,
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)
println(squares) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
val cubes = numberFunc((xs, x) => xs :+ x*x*x)
println(cubes) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
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)
println(squares) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
val cubes = numberFunc((xs, x) => xs :+ x*x*x)
println(cubes) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
Comparison with âcurryingâ
You may sometimes see a method with multiple parameter lists referred to as âcurriedâ.
As the Wikipedia article on currying states,
Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument
We discourage the use of the word âcurryâ in reference to Scalaâs multiple parameter lists, for two reasons:
1) In Scala, multiple parameters and multiple parameter lists are specified and implemented directly, as part of the language, rather being derived from single-parameter functions.
2) There is danger of confusion with the Scala standard libraryâs curried
and uncurried
methods, which donât involve multiple parameter lists at all.
Regardless, there are certainly similarities to be found between multiple parameter lists and currying. Though they are different at the definition site, the call site might nonetheless look identical, as in this example:
// version with multiple parameter lists
def addMultiple(n1: Int)(n2: Int) = n1 + n2
// two different ways of arriving at a curried version instead
def add(n1: Int, n2: Int) = n1 + n2
val addCurried1 = (add _).curried
val addCurried2 = (n1: Int) => (n2: Int) => n1 + n2
// regardless, all three call sites are identical
addMultiple(3)(4) // 7
addCurried1(3)(4) // 7
addCurried2(3)(4) // 7
Contributors to this page:
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4