diff --git a/_tour/implicit-parameters.md b/_tour/implicit-parameters.md index 4d5977f24..2a5063588 100644 --- a/_tour/implicit-parameters.md +++ b/_tour/implicit-parameters.md @@ -10,7 +10,7 @@ previous-page: self-types redirect_from: "/tutorials/tour/implicit-parameters.html" --- -A method can have *contextual parameters*, also called *implicit parameters*, or more concisely *implicits*. +A method can have _contextual parameters_, also called _implicit parameters_, or more concisely _implicits_. Parameter lists starting with the keyword `using` (or `implicit` in Scala 2) mark contextual parameters. Unless the call site explicitly provides arguments for those parameters, Scala will look for implicitly available `given` (or `implicit` in Scala 2) values of the correct type. If it can find appropriate values, it automatically passes them. @@ -19,11 +19,12 @@ This is best shown using a small example first. We define an interface `Comparator[A]` that can compare elements of type `A`, and provide two implementations, for `Int`s and `String`s. We then define a method `max[A](x: A, y: A)` that returns the greater of the two arguments. Since `x` and `y` are generically typed, in general we do not know how to compare them, but we can ask for an appropriate comparator. -As there is typically a canonical comparator for any given type `A`, we can declare them as *given*s, or *implicitly* available. +As there is typically a canonical comparator for any given type `A`, we can declare them as *given*s, or _implicitly_ available. {% tabs implicits-comparator class=tabs-scala-version %} {% tab 'Scala 2' for=implicits-comparator %} + ```scala mdoc trait Comparator[A] { def compare(x: A, y: A): Int @@ -48,10 +49,8 @@ println(max("hello", "world")) // world ``` ```scala mdoc:fail -// does not compile: println(max(false, true)) -// ^ -// error: could not find implicit value for parameter comparator: Comparator[Boolean] +// error: could not find implicit value for parameter comparator: Comparator[Boolean] ``` The `comparator` parameter is automatically filled in with `Comparator.IntComparator` for `max(10, 6)`, and with `Comparator.StringComparator` for `max("hello", "world")`. @@ -59,6 +58,7 @@ Since no implicit `Comparator[Boolean]` can be found, the call `max(false, true) {% endtab %} {% tab 'Scala 3' for=implicits-comparator %} + ```scala trait Comparator[A]: def compare(x: A, y: A): Int @@ -79,13 +79,9 @@ println(max(10, 6)) // 10 println(max("hello", "world")) // world ``` -```scala -// does not compile: +```scala mdoc:fail println(max(false, true)) --- Error: ---------------------------------------------------------------------- -1 |println(max(false, true)) - | ^ - |no given instance of type Comparator[Boolean] was found for parameter comparator of method max +// error: could not find implicit value for parameter comparator: Comparator[Boolean] ``` The `comparator` parameter is automatically filled in with the `given Comparator[Int]` for `max(10, 6)`, and with the `given Comparator[String]` for `max("hello", "world")`. @@ -96,7 +92,45 @@ Since no `given Comparator[Boolean]` can be found, the call `max(false, true)` f Scala will look for available given values in two places: -* Scala will first look for given definitions and using parameters that can be accessed directly (without a prefix) at the call site of `max`. -* Then it looks for members marked `given`/`implicit` in the companion objects associated with the implicit candidate type (for example: `object Comparator` for the candidate type `Comparator[Int]`). +- Scala will first look for given definitions and using parameters that can be accessed directly (without a prefix) at the call site of `max`. +- Then it looks for members marked `given`/`implicit` in the companion objects associated with the implicit candidate type (for example: `object Comparator` for the candidate type `Comparator[Int]`). A more detailed guide to where Scala looks for implicits can be found in [the FAQ](/tutorials/FAQ/finding-implicits.html). + +### Context Bounds + +Context bounds are especially useful when working with type classes, where a type requires an associated contextual value. + +A _context bound_ is a concise way to express that a type requires an implicit (contextual) value, +without explicitly naming the parameter. + +{% tabs context-bounds-max class=tabs-scala-version %} + +{% tab 'Scala 2' for=context-bounds-max %} + +```scala mdoc +def max[A: Comparator](x: A, y: A): A = { + val comparator = implicitly[Comparator[A]] + if (comparator.compare(x, y) >= 0) x else y +} +``` + +{% endtab %} + +{% tab 'Scala 3' for=context-bounds-max %} + +```scala +def max[A: Comparator](x: A, y: A): A = + val comparator = summon[Comparator[A]] + if comparator.compare(x, y) >= 0 then x else y +``` + +{% endtab %} + +{% endtabs %} + +A context bound `[A: Comparator]` is syntactic sugar for: + +```scala +[A](using comparator: Comparator[A]) +``` diff --git a/_tour/multiple-parameter-lists.md b/_tour/multiple-parameter-lists.md index 324795b6c..4b4237c40 100644 --- a/_tour/multiple-parameter-lists.md +++ b/_tour/multiple-parameter-lists.md @@ -19,6 +19,7 @@ Here is an example, as defined on the `Iterable` trait in Scala's collections AP {% tabs foldLeft_definition class=tabs-scala-version %} {% tab 'Scala 2' for=foldLeft_definition %} + ```scala trait Iterable[A] { ... @@ -26,15 +27,18 @@ 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 %} @@ -46,11 +50,13 @@ Starting with an initial value of 0, `foldLeft` here applies the function `(m, n {% 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 %} @@ -61,15 +67,24 @@ Suggested use cases for multiple parameter lists include: #### Drive type inference -It so happens that in Scala, type inference proceeds one parameter list at a time. +In Scala 2, type inference proceeds one parameter list at a time. + +> Scala 3 note : +> This explanation does not apply to Scala 3. +> In Scala 3, type variables are inferred as late as possible and are not +> constrained by parameter list boundaries. As a result, later parameter +> lists may contribute to type inference. + 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 %} @@ -79,9 +94,11 @@ Then you'd like to call it in the following way, but will find that it doesn't c {% tabs foldLeft1_wrong_use %} {% tab 'Scala 2 and 3' for=foldLeft1_wrong_use %} + ```scala mdoc:fail def notPossible = foldLeft1(numbers, 0, _ + _) ``` + {% endtab %} {% endtabs %} @@ -91,10 +108,12 @@ 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 %} @@ -104,17 +123,18 @@ That's because Scala won't be able to infer the type of the function `_ + _`, as {% 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. - #### Implicit parameters To specify only certain parameters as [`implicit`](https://docs.scala-lang.org/tour/implicit-parameters.html), they must be placed in their own `implicit` parameter list. @@ -124,15 +144,19 @@ 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 %} @@ -146,6 +170,7 @@ For example, {% tabs foldLeft_partial class=tabs-scala-version %} {% tab 'Scala 2' 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]()) _ @@ -156,9 +181,11 @@ 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 %} {% tab 'Scala 3' for=foldLeft_partial %} + ```scala val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val numberFunc = numbers.foldLeft(List[Int]()) @@ -169,6 +196,7 @@ 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 %} @@ -185,13 +213,13 @@ As the [Wikipedia article on currying](https://en.wikipedia.org/wiki/Currying) s 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. +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`](https://www.scala-lang.org/api/current/scala/Function2.html#curried:T1=%3E(T2=%3ER)) -and [`uncurried`](https://www.scala-lang.org/api/current/scala/Function$.html#uncurried[T1,T2,R](f:T1=%3E(T2=%3ER)):(T1,T2)=%3ER) methods, which don't involve multiple parameter lists at all. +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 @@ -201,6 +229,7 @@ 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 @@ -213,6 +242,7 @@ addMultiple(3)(4) // 7 addCurried1(3)(4) // 7 addCurried2(3)(4) // 7 ``` + {% endtab %} {% endtabs %}