Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions _tour/implicit-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -48,17 +49,16 @@ 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")`.
Since no implicit `Comparator[Boolean]` can be found, the call `max(false, true)` fails to compile.
{% endtab %}

{% tab 'Scala 3' for=implicits-comparator %}

```scala
trait Comparator[A]:
def compare(x: A, y: A): Int
Expand All @@ -79,13 +79,9 @@ println(max(10, 6)) // 10
println(max("hello", "world")) // world
```

```scala
// does not compile:
```scala mdoc:fail

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesnt include the error in the output, but in this case the error message is the point

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")`.
Expand All @@ -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])
```
46 changes: 38 additions & 8 deletions _tour/multiple-parameter-lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,26 @@ 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] {
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
}
```

{% endtab %}

{% tab 'Scala 3' for=foldLeft_definition %}

```scala
trait Iterable[A]:
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
```

{% endtab %}

{% endtabs %}
Expand All @@ -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 %}
Expand All @@ -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 %}
Expand All @@ -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 %}
Expand All @@ -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 %}
Expand All @@ -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.
Expand All @@ -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 %}
Expand All @@ -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]()) _
Expand All @@ -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]())
Expand All @@ -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 %}
Expand All @@ -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`](<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.

Regardless, there are certainly similarities to be found between
multiple parameter lists and currying. Though they are different at
Expand All @@ -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
Expand All @@ -213,6 +242,7 @@ addMultiple(3)(4) // 7
addCurried1(3)(4) // 7
addCurried2(3)(4) // 7
```

{% endtab %}

{% endtabs %}
Loading