Skip to content

Commit 836e98b

Browse files
committed
Add Scala 2 tabs and explanations in the Implicit Conversions page
Remove the detailed explanation from the Tour page in favor of the Book.
1 parent da3b344 commit 836e98b

File tree

2 files changed

+198
-113
lines changed

2 files changed

+198
-113
lines changed
Lines changed: 197 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,223 @@
11
---
22
title: Implicit Conversions
33
type: section
4-
description: This page demonstrates how to implement Implicit Conversions in Scala 3.
4+
description: This page demonstrates how to implement Implicit Conversions in Scala.
55
languages: [zh-cn]
66
num: 65
77
previous-page: ca-multiversal-equality
88
next-page: ca-summary
99
---
1010

11+
Implicit conversions are a powerful Scala feature that allows users to supply an argument
12+
of one type as if it were another type, to avoid boilerplate.
1113

12-
Implicit conversions are defined by `given` instances of the `scala.Conversion` class.
13-
For example, not accounting for possible conversion errors, this code defines an implicit conversion from `String` to `Int`:
14+
> Note that in Scala 2, implicit conversions were also used to provide additional members
15+
> to closed classes (see [Implicit Classes]({% link _overviews/core/implicit-classes.md %})).
16+
> In Scala 3, we recommend to address this use-case by defining [extension methods] instead
17+
> of implicit conversions (although the standard library still relies on implicit conversions
18+
> for historical reasons).
19+
20+
## Example
21+
22+
Consider for instance a method `findUserById` that takes a parameter of type `Long`:
23+
24+
{% tabs implicit-conversions-1 %}
25+
{% tab 'Scala 2 and 3' %}
26+
~~~ scala
27+
def findUserById(id: Long): Option[User]
28+
~~~
29+
{% endtab %}
30+
{% endtabs %}
31+
32+
We omit the definition of the type `User` for the sake of brevity, it does not matter for
33+
our example.
34+
35+
In Scala, it is possible to call the method `findUserById` with an argument of type `Int`
36+
instead of the expected type `Long`, because the argument will be implicitly converted
37+
into the type `Long`:
38+
39+
{% tabs implicit-conversions-2 %}
40+
{% tab 'Scala 2 and 3' %}
41+
~~~ scala
42+
val id: Int = 42
43+
findUserById(id) // OK
44+
~~~
45+
{% endtab %}
46+
{% endtabs %}
47+
48+
This code does not fail to compile with an error like “type mismatch: expected `Long`,
49+
found `Int`” because there is an implicit conversion that converts the argument `id`
50+
to a value of type `Long`.
51+
52+
## Detailed Explanation
53+
54+
This section describes how to define and use implicit conversions.
55+
56+
### Defining an Implicit Conversion
57+
58+
{% tabs implicit-conversions-3 class=tabs-scala-version %}
59+
60+
{% tab 'Scala 2' %}
61+
In Scala 2, an implicit conversion from type `S` to type `T` is defined by an
62+
[implicit class]({% link _overviews/core/implicit-classes.md %}) `T` that takes
63+
a single constructor parameter of type `S`, an
64+
[implicit value]({% link _overviews/scala3-book/ca-given-using-clauses.md %}) of
65+
function type `S => T`, or by an implicit method convertible to a value of that type.
66+
67+
For example, the following code defines an implicit conversion from `Int` to `Long`:
68+
69+
~~~ scala
70+
import scala.language.implicitConversions
71+
72+
implicit def int2long(x: Int): Long = x.toLong
73+
~~~
74+
75+
This is an implicit method convertible to a value of type `Int => Long`.
76+
77+
See the section “Beware the Power of Implicit Conversions” below for an
78+
explanation of the clause `import scala.language.implicitConversions`
79+
at the beginning.
80+
{% endtab %}
81+
82+
{% tab 'Scala 3' %}
83+
In Scala 3, an implicit conversion from type `S` to type `T` is defined by a
84+
[`given` instance]({% link _overviews/scala3-book/ca-given-using-clauses.md %})
85+
of type `scala.Conversion[S, T]`. For compatibility with Scala 2, it can also
86+
be defined by an implicit method (read more in the Scala 2 tab).
87+
88+
For example, this code defines an implicit conversion from `Int` to `Long`:
1489

1590
```scala
16-
given Conversion[String, Int] with
17-
def apply(s: String): Int = Integer.parseInt(s)
91+
given int2long: Conversion[Int, Long] with
92+
def apply(x: Int): Long = x.toLong
1893
```
1994

20-
Using an alias this can be expressed more concisely as:
95+
Like other given definitions, implicit conversions can be anonymous:
96+
97+
~~~ scala
98+
given Conversion[Int, Long] with
99+
def apply(x: Int): Long = x.toLong
100+
~~~
101+
102+
Using an alias, this can be expressed more concisely as:
21103

22104
```scala
23-
given Conversion[String, Int] = Integer.parseInt(_)
105+
given Conversion[Int, Long] = (x: Int) => x.toLong
24106
```
107+
{% endtab %}
25108

26-
Using either of those conversions, you can now use a `String` in places where an `Int` is expected:
109+
{% endtabs %}
27110

28-
```scala
111+
### Using an Implicit Conversion
112+
113+
Implicit conversions are applied in two situations:
114+
115+
1. If an expression `e` is of type `S`, and `S` does not conform to the expression's expected type `T`.
116+
2. In a selection `e.m` with `e` of type `S`, if the selector `m` does not denote a member of `S`
117+
(to support Scala-2-style [extension methods]).
118+
119+
In the first case, a conversion `c` is searched for, which is applicable to `e` and whose result type conforms to `T`.
120+
121+
In our example above, when we pass the argument `id` of type `Int` to the method `findUserById`,
122+
the implicit conversion `int2long(id)` is inserted.
123+
124+
In the second case, a conversion `c` is searched for, which is applicable to `e` and whose result contains a member named `m`.
125+
126+
An example is to compare two strings `"foo" < "bar"`. In this case, `String` has no member `<`, so the implicit conversion `Predef.augmentString("foo") < "bar"` is inserted. (`scala.Predef` is automatically imported into all Scala programs.)
127+
128+
### How Are Implicit Conversions Brought Into Scope?
129+
130+
When the compiler searches for applicable conversions:
131+
132+
- first, it looks into the current lexical scope
133+
- implicit conversions defined in the current scope or the outer scopes
134+
- imported implicit conversions
135+
- implicit conversions imported by a wildcard import (Scala 2 only)
136+
- then, it looks into the [companion objects] _associated_ with the argument
137+
type `S` or the expected type `T`. The companion objects associated with
138+
a type `X` are:
139+
- the companion object `X` itself
140+
- the companion objects associated with any of `X`’s inherited types
141+
- the companion objects associated with any type argument in `X`
142+
- if `X` is an inner class, the outer objects in which it is embedded
143+
144+
For instance, consider an implicit conversion `fromStringToUser` defined in an
145+
object `Conversions`:
146+
147+
{% tabs implicit-conversions-4 class=tabs-scala-version %}
148+
{% tab 'Scala 2' %}
149+
~~~ scala
29150
import scala.language.implicitConversions
30151

31-
// a method that expects an Int
32-
def plus1(i: Int) = i + 1
152+
object Conversions {
153+
implicit def fromStringToUser(name: String): User = (name: String) => User(name)
154+
}
155+
~~~
156+
{% endtab %}
157+
{% tab 'Scala 3' %}
158+
~~~ scala
159+
object Conversions:
160+
given fromStringToUser: Conversion[String, User] = (name: String) => User(name)
161+
~~~
162+
{% endtab %}
163+
{% endtabs %}
33164

34-
// pass it a String that converts to an Int
35-
plus1("1")
36-
```
165+
The following imports would equivalently bring the conversion into scope:
37166

38-
> Note the clause `import scala.language.implicitConversions` at the beginning,
39-
> to enable implicit conversions in the file.
167+
{% tabs implicit-conversions-5 class=tabs-scala-version %}
168+
{% tab 'Scala 2' %}
169+
~~~ scala
170+
import Conversions.fromStringToUser
171+
// or
172+
import Conversions._
173+
~~~
174+
{% endtab %}
175+
{% tab 'Scala 3' %}
176+
~~~ scala
177+
import Conversions.fromStringToUser
178+
// or
179+
import Conversions.given
180+
// or
181+
import Conversions.{given Conversion[String, User]}
182+
~~~
40183

41-
## Discussion
184+
Note that in Scala 3, a wildcard import (ie `import Conversions.*`) does not import given
185+
definitions.
186+
{% endtab %}
187+
{% endtabs %}
42188

43-
The Predef package contains “auto-boxing” conversions that map primitive number types to subclasses of `java.lang.Number`.
44-
For instance, the conversion from `Int` to `java.lang.Integer` can be defined as follows:
189+
In the introductory example, the conversion from `Int` to `Long` does not require an import
190+
because it is defined in the object `Int`, which is the companion object of the type `Int`.
45191

46-
```scala
47-
given int2Integer: Conversion[Int, java.lang.Integer] =
48-
java.lang.Integer.valueOf(_)
49-
```
192+
Further reading:
193+
[Where does Scala look for implicits? (on Stackoverflow)](https://stackoverflow.com/a/5598107).
194+
195+
### Beware the Power of Implicit Conversions
196+
197+
{% tabs implicit-conversions-6 class=tabs-scala-version %}
198+
{% tab 'Scala 2' %}
199+
Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition.
200+
201+
To turn off the warnings take either of these actions:
202+
203+
* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition
204+
* Invoke the compiler with `-language:implicitConversions`
205+
206+
No warning is emitted when the conversion is applied by the compiler.
207+
{% endtab %}
208+
{% tab 'Scala 3' %}
209+
Because implicit conversions can have pitfalls if used indiscriminately the compiler warns in two situations:
210+
- when compiling a Scala 2 style implicit conversion definition.
211+
- at the call site where a given instance of `scala.Conversion` is inserted as a conversion.
212+
213+
To turn off the warnings take either of these actions:
214+
215+
- Import `scala.language.implicitConversions` into the scope of:
216+
- a Scala 2 style implicit conversion definition
217+
- call sites where a given instance of `scala.Conversion` is inserted as a conversion.
218+
- Invoke the compiler with `-language:implicitConversions`
219+
{% endtab %}
220+
{% endtabs %}
221+
222+
[extension methods]: {% link _overviews/scala3-book/ca-extension-methods.md %}
223+
[companion objects]: {% link _overviews/scala3-book/domain-modeling-tools.md %}#companion-objects

_tour/implicit-conversions.md

Lines changed: 1 addition & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -38,95 +38,6 @@ In the second case, a conversion `c` is searched for, which is applicable to `e`
3838

3939
An example is to compare two strings `"foo" < "bar"`. In this case, `String` has no member `<`, so the implicit conversion `Predef.augmentString("foo") < "bar"` is inserted. (`scala.Predef` is automatically imported into all Scala programs.)
4040

41-
### How are implicit conversions brought into scope? ###
42-
43-
{% tabs implicit-conversion-scope class=tabs-scala-version %}
44-
{% tab 'Scala 2' %}
45-
In Scala 2, an implicit conversion is brought into scope by importing from the object that defined it, (e.g. `Conversions` in this case). If the implicit conversion is in the companion object of the argument type, (e.g. `Student` in this case), then no import is necessary.
46-
47-
```scala
48-
import scala.language.implicitConversions // required to define an implicit conversion
49-
50-
case class Student(name: String)
51-
object Student {
52-
implicit def fromStudentToInt(student: Student): Int = student.name.length
53-
}
54-
55-
object Conversions {
56-
implicit def fromStringToStudent(name: String): Student = Student(name)
57-
}
58-
59-
import Conversions._
60-
object Usage {
61-
def main(args: Array[String]) = {
62-
val reginald: Student = "Reginald" // applies the conversion Conversions.fromStringToStudent
63-
println(reginald + 2) // applies the conversion Student.fromStudentToInt
64-
}
65-
}
66-
```
67-
{% endtab %}
68-
{% tab 'Scala 3' %}
69-
In Scala 3, an implicit conversion is brought into scope by either importing `given` or the named conversion from the object that defined it, (e.g. `Conversions` in this case).
70-
71-
Note that as of Scala 3, implicit conversions cannot be brought into scope anymore by means of a wildcard import (`*`).
72-
73-
Given the example:
74-
75-
```scala
76-
case class Student(name: String):
77-
def printName: Unit = println(name)
78-
object Student:
79-
given Conversion[Student, Int] = _.name.length
80-
81-
object Conversions:
82-
given fromStringToStudent: Conversion[String, Student] = Student(_)
83-
```
84-
85-
The following imports would bring the `Conversion[String, Student]` into scope:
86-
- `import Conversions.given`
87-
- `import Conversions.{given Conversion[String, Student]}`
88-
- `import Conversions.fromStringToStudent`
89-
90-
If the implicit conversion is in the companion object of the argument type, (e.g. `Student` in this case), then no import is necessary.
91-
92-
```scala
93-
import Conversions.given
94-
object Usage:
95-
@main def run =
96-
val reginald: Student = "Reginald" // applies the Conversion[String, Student]
97-
println(reginald + 2) // applies the Conversion[Student, Int]
98-
```
99-
{% endtab %}
100-
{% endtabs %}
101-
102-
Further reading:
103-
- [Where does Scala look for implicits? (on StackOverflow)](https://docs.scala-lang.org/tutorials/FAQ/index.html#where-does-scala-look-for-implicits).
104-
105-
### Beware the power of implicit conversions
106-
107-
{% tabs implicit-conversion-warning class=tabs-scala-version %}
108-
{% tab 'Scala 2' %}
109-
Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition.
110-
111-
To turn off the warnings take either of these actions:
112-
113-
* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition
114-
* Invoke the compiler with `-language:implicitConversions`
115-
116-
No warning is emitted when the conversion is applied by the compiler.
117-
{% endtab %}
118-
{% tab 'Scala 3' %}
119-
Because implicit conversions can have pitfalls if used indiscriminately the compiler warns in two situations:
120-
- when compiling a Scala 2 style implicit conversion definition.
121-
- at the call site where a given instance of `scala.Conversion` is inserted as a conversion.
122-
123-
To turn off the warnings take either of these actions:
124-
125-
- Import `scala.language.implicitConversions` into the scope of:
126-
- a Scala 2 style implicit conversion definition
127-
- call sites where a given instance of `scala.Conversion` is inserted as a conversion.
128-
- Invoke the compiler with `-language:implicitConversions`
129-
{% endtab %}
130-
{% endtabs %}
41+
Further reading: [Implicit Conversions (in the Scala book)]({% link _overviews/scala3-book/ca-implicit-conversions.md %}).
13142

13243
[exts]: {% link _overviews/scala3-book/ca-extension-methods.md %}

0 commit comments

Comments
 (0)