From 308327cffd678174066af32be536492d1e30dc7b Mon Sep 17 00:00:00 2001 From: Luc Henninger Date: Fri, 16 Sep 2022 18:20:58 +0200 Subject: [PATCH] Update multiple-parameter-lists.md --- _tour/multiple-parameter-lists.md | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/_tour/multiple-parameter-lists.md b/_tour/multiple-parameter-lists.md index 6bb46d420c..632a5ab4fc 100644 --- a/_tour/multiple-parameter-lists.md +++ b/_tour/multiple-parameter-lists.md @@ -16,6 +16,9 @@ Methods may have multiple parameter lists. Here is an example, as defined on the `Iterable` trait in Scala's collections API: +{% tabs foldLeft_definition class=tabs-scala-version %} + +{% tab 'Scala 2' for=foldLeft_definition %} ```scala trait Iterable[A] { ... @@ -23,16 +26,34 @@ trait Iterable[A] { ... } ``` +{% endtab %} + +{% tab 'Scala 3' for=foldLeft_definition %} +```scala +trait Iterable[A]: + ... + def foldLeft[B](z: B)(op: (B, A) => B): B + ... +``` +{% endtab %} + +{% endtabs %} `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. +{% tabs foldLeft_use %} + +{% tab 'Scala 2 and 3' for=foldLeft_use %} ```scala mdoc 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 ``` +{% endtab %} + +{% endtabs %} ### Use cases @@ -43,29 +64,53 @@ Suggested use cases for multiple parameter lists include: It so happens that in Scala, type inference proceeds one parameter list at a time. Say you have the following method: +{% tabs foldLeft1_definition %} + +{% tab 'Scala 2 and 3' for=foldLeft1_definition %} ```scala mdoc def foldLeft1[A, B](as: List[A], b0: B, op: (B, A) => B) = ??? ``` +{% endtab %} + +{% endtabs %} Then you'd like to call it in the following way, but will find that it doesn't compile: +{% tabs foldLeft1_wrong_use %} + +{% tab 'Scala 2 and 3' for=foldLeft1_wrong_use %} ```scala mdoc:fail def notPossible = foldLeft1(numbers, 0, _ + _) ``` +{% endtab %} + +{% endtabs %} you will have to call it like one of the below ways: +{% tabs foldLeft1_good_use %} + +{% tab 'Scala 2 and 3' for=foldLeft1_good_use %} ```scala mdoc def firstWay = foldLeft1[Int, Int](numbers, 0, _ + _) def secondWay = foldLeft1(numbers, 0, (a: Int, b: Int) => a + b) ``` +{% endtab %} + +{% endtabs %} 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` +{% tabs foldLeft2_definition_and_use %} + +{% tab 'Scala 2 and 3' for=foldLeft2_definition_and_use %} ```scala mdoc def foldLeft2[A, B](as: List[A], b0: B)(op: (B, A) => B) = ??? def possible = foldLeft2(numbers, 0)(_ + _) ``` +{% endtab %} + +{% endtabs %} This definition doesn't need any type hints and can infer all of its type parameters. @@ -76,9 +121,21 @@ To specify only certain parameters as [`implicit`](https://docs.scala-lang.org/t An example of this is: +{% tabs execute_definition class=tabs-scala-version %} + +{% tab 'Scala 2' for=execute_definition %} ```scala mdoc def execute(arg: Int)(implicit ec: scala.concurrent.ExecutionContext) = ??? ``` +{% endtab %} + +{% tab 'Scala 3' for=execute_definition %} +```scala +def execute(arg: Int)(using ec: scala.concurrent.ExecutionContext) = ??? +``` +{% endtab %} + +{% endtabs %} #### Partial application @@ -86,6 +143,9 @@ When a method is called with a fewer number of parameter lists, then this will y For example, +{% tabs foldLeft_partial %} + +{% tab 'Scala 2 and 3' for=foldLeft_partial %} ```scala mdoc:nest val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val numberFunc = numbers.foldLeft(List[Int]()) _ @@ -96,6 +156,9 @@ 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) ``` +{% endtab %} + +{% endtabs %} ### Comparison with "currying" @@ -122,6 +185,9 @@ multiple parameter lists and currying. Though they are different at the definition site, the call site might nonetheless look identical, as in this example: +{% tabs about_currying %} + +{% tab 'Scala 2 and 3' for=about_currying %} ```scala mdoc // version with multiple parameter lists def addMultiple(n1: Int)(n2: Int) = n1 + n2 @@ -134,3 +200,6 @@ addMultiple(3)(4) // 7 addCurried1(3)(4) // 7 addCurried2(3)(4) // 7 ``` +{% endtab %} + +{% endtabs %}