Skip to content

Commit 6d603cc

Browse files
sjrdjulienrf
authored andcommitted
Tabbed code snippets for the implicit params tour page.
1 parent 29a4ff7 commit 6d603cc

File tree

1 file changed

+72
-40
lines changed

1 file changed

+72
-40
lines changed

_tour/implicit-parameters.md

+72-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: tour
3-
title: Implicit Parameters
3+
title: Contextual Parameters, aka Implicit Parameters
44
partof: scala-tour
55

66
num: 28
@@ -10,61 +10,93 @@ previous-page: self-types
1010
redirect_from: "/tutorials/tour/implicit-parameters.html"
1111
---
1212

13-
A method can have an _implicit_ parameter list, marked by the _implicit_ keyword at the start of the parameter list. If the parameters in that parameter list are not passed as usual, Scala will look if it can get an implicit value of the correct type, and if it can, pass it automatically.
13+
A method can have *contextual parameters*, also called *implicit parameters*, or more concisely *implicits*.
14+
Parameter lists starting with the keyword `using` (or `implicit` in Scala 2) mark contextual parameters.
15+
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.
16+
If it can find appropriate values, it automatically passes them.
1417

15-
The places Scala will look for these parameters fall into two categories:
18+
This is best shown using a small example first.
19+
We define an interface `Comparator[A]` that can compare elements of type `A`, and provide two implementations, for `Int`s and `String`s.
20+
We then define a method `max[A](x: A, y: A)` that returns the greater of the two arguments.
21+
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.
22+
As there is typically a canonical comparator for any given type `A`, we can declare them as *given*s, or *implicitly* available.
1623

17-
* Scala will first look for implicit definitions and implicit parameters that can be accessed directly (without a prefix) at the point the method with the implicit parameter block is called.
18-
* Then it looks for members marked implicit in all the companion objects associated with the implicit candidate type.
19-
20-
A more detailed guide to where Scala looks for implicits can be found in [the FAQ](//docs.scala-lang.org/tutorials/FAQ/finding-implicits.html).
21-
22-
In the following example we define a method `sum` which computes the sum of a list of elements using the monoid's `add` and `unit` operations. Please note that implicit values cannot be top-level.
24+
{% tabs implicits-comparator class=tabs-scala-version %}
2325

26+
{% tab 'Scala 2' for=implicits-comparator %}
2427
```scala mdoc
25-
abstract class Monoid[A] {
26-
def add(x: A, y: A): A
27-
def unit: A
28+
trait Comparator[A] {
29+
def compare(x: A, y: A): Int
2830
}
2931

30-
object ImplicitTest {
31-
implicit val stringMonoid: Monoid[String] = new Monoid[String] {
32-
def add(x: String, y: String): String = x concat y
33-
def unit: String = ""
34-
}
35-
36-
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
37-
def add(x: Int, y: Int): Int = x + y
38-
def unit: Int = 0
32+
object Comparator {
33+
implicit object IntComparator extends Comparator[Int] {
34+
def compare(x: Int, y: Int): Int = Integer.compare(x, y)
3935
}
40-
41-
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
42-
if (xs.isEmpty) m.unit
43-
else m.add(xs.head, sum(xs.tail))
44-
45-
def main(args: Array[String]): Unit = {
46-
println(sum(List(1, 2, 3))) // uses intMonoid implicitly
47-
println(sum(List("a", "b", "c"))) // uses stringMonoid implicitly
36+
37+
implicit object StringComparator extends Comparator[String] {
38+
def compare(x: String, y: String): Int = x.compareTo(y)
4839
}
4940
}
50-
```
5141

52-
`Monoid` defines an operation called `add` here, that combines a pair of `A`s and returns another `A`, together with an operation called `unit` that is able to create some (specific) `A`.
42+
def max[A](x: A, y: A)(implicit comparator: Comparator[A]): A =
43+
if (comparator.compare(x, y) >= 0) x
44+
else y
45+
46+
println(max(10, 6)) // 10
47+
println(max("hello", "world")) // world
48+
```
5349

54-
To show how implicit parameters work, we first define monoids `stringMonoid` and `intMonoid` for strings and integers, respectively. The `implicit` keyword indicates that the corresponding object can be used implicitly.
50+
```scala mdoc:fail
51+
// does not compile:
52+
println(max(false, true))
53+
// ^
54+
// error: could not find implicit value for parameter comparator: Comparator[Boolean]
55+
```
5556

56-
The method `sum` takes a `List[A]` and returns an `A`, which takes the initial `A` from `unit`, and combines each next `A` in the list to that with the `add` method. Making the parameter `m` implicit here means we only have to provide the `xs` parameter when we call the method if Scala can find an implicit `Monoid[A]` to use for the implicit `m` parameter.
57+
The `comparator` parameter is automatically filled in with `Comparator.IntComparator` for `max(10, 6)`, and with `Comparator.StringComparator` for `max("hello", "world")`.
58+
Since no implicit `Comparator[Boolean]` can be found, the call `max(false, true)` fails to compile.
59+
{% endtab %}
5760

58-
In our `main` method we call `sum` twice, and only provide the `xs` parameter. Scala will now look for an implicit in the scope mentioned above. The first call to `sum` passes a `List[Int]` for `xs`, which means that `A` is `Int`. The implicit parameter list with `m` is left out, so Scala will look for an implicit of type `Monoid[Int]`. The first lookup rule reads
61+
{% tab 'Scala 3' for=implicits-comparator %}
62+
```scala
63+
trait Comparator[A]:
64+
def compare(x: A, y: A): Int
5965

60-
> Scala will first look for implicit definitions and implicit parameters that can be accessed directly (without a prefix) at the point the method with the implicit parameter block is called.
66+
object Comparator:
67+
given Comparator[Int] with
68+
def compare(x: Int, y: Int): Int = Integer.compare(x, y)
6169

62-
`intMonoid` is an implicit definition that can be accessed directly in `main`. It is also of the correct type, so it's passed to the `sum` method automatically.
70+
given Comparator[String] with
71+
def compare(x: String, y: String): Int = x.compareTo(y)
72+
end Comparator
6373

64-
The second call to `sum` passes a `List[String]`, which means that `A` is `String`. Implicit lookup will go the same way as with `Int`, but will this time find `stringMonoid`, and pass that automatically as `m`.
74+
def max[A](x: A, y: A)(using comparator: Comparator[A]): A =
75+
if comparator.compare(x, y) >= 0 then x
76+
else y
6577

66-
The program will output
78+
println(max(10, 6)) // 10
79+
println(max("hello", "world")) // world
6780
```
68-
6
69-
abc
81+
82+
```scala
83+
// does not compile:
84+
println(max(false, true))
85+
-- Error: ----------------------------------------------------------------------
86+
1 |println(max(false, true))
87+
| ^
88+
|no given instance of type Comparator[Boolean] was found for parameter comparator of method max
7089
```
90+
91+
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")`.
92+
Since no `given Comparator[Boolean]` can be found, the call `max(false, true)` fails to compile.
93+
{% endtab %}
94+
95+
{% endtabs %}
96+
97+
Scala will look for available given values in two places:
98+
99+
* Scala will first look for given definitions and using parameters that can be accessed directly (without a prefix) at the call site of `max`.
100+
* 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]`).
101+
102+
A more detailed guide to where Scala looks for implicits can be found in [the FAQ](//docs.scala-lang.org/tutorials/FAQ/finding-implicits.html).

0 commit comments

Comments
 (0)