Skip to content

Commit 27d84a9

Browse files
authored
Merge pull request #5347 from dotty-staging/details-implicit-conversions
More details on implicit conversions
2 parents 39bb8bd + 844ad0e commit 27d84a9

File tree

2 files changed

+167
-31
lines changed

2 files changed

+167
-31
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
layout: doc-page
3+
title: "Implicit Conversions - More Details"
4+
---
5+
6+
## Implementation
7+
8+
An implicit conversion, or _view_, from type `S` to type `T` is
9+
defined by either:
10+
11+
- An `implicit def` which has type `S => T` or `(=> S) => T`
12+
- An implicit value which has type `ImplicitConverter[S, T]`
13+
14+
The standard library defines an abstract class `ImplicitConverter`:
15+
16+
```scala
17+
abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
18+
```
19+
20+
Function literals are automatically converted to `ImplicitConverter`
21+
values.
22+
23+
Views are applied in three situations:
24+
25+
1. If an expression `e` is of type `T`, and `T` does not conform to
26+
the expression's expected type `pt`. In this case, an implicit `v`
27+
which is applicable to `e` and whose result type conforms to `pt`
28+
is searched. The search proceeds as in the case of implicit
29+
parameters, where the implicit scope is the one of `T => pt`. If
30+
such a view is found, the expression `e` is converted to `v(e)`.
31+
1. In a selection `e.m` with `e` of type `T`, if the selector `m` does
32+
not denote an accessible member of `T`. In this case, a view `v`
33+
which is applicable to `e` and whose result contains an accessible
34+
member named `m` is searched. The search proceeds as in the case of
35+
implicit parameters, where the implicit scope is the one of `T`. If
36+
such a view is found, the selection `e.m` is converted to `v(e).m`.
37+
1. In an application `e.m(args)` with `e` of type `T`, if the selector
38+
`m` denotes some accessible member(s) of `T`, but none of these
39+
members is applicable to the arguments `args`. In this case, a view
40+
`v` which is applicable to `e` and whose result contains a method
41+
`m` which is applicable to `args` is searched. The search proceeds
42+
as in the case of implicit parameters, where the implicit scope is
43+
the one of `T`. If such a view is found, the application
44+
`e.m(args)` is converted to `v(e).m(args)`.
45+
46+
# Differences with Scala 2 implicit conversions
47+
48+
In Scala 2, views whose parameters are passed by-value take precedence
49+
over views whose parameters are passed by-name. This is no longer the
50+
case in Scala 3. A type error reporting the ambiguous conversions will
51+
be emitted in cases where this rule would be applied in Scala 2:
52+
53+
```scala
54+
implicit def conv1(x: Int): String = x.toString
55+
implicit def conv2(x: => Int): String = x.toString
56+
57+
val x: String = 0 // Compiles in Scala2 (uses `conv1`),
58+
// type error in Scala 3 because of ambiguity.
59+
```
60+
61+
In Scala 2, implicit values of a function type would be considered as
62+
potential views. In Scala 3, these implicit value need to have type
63+
`ImplicitConverter`:
64+
65+
```scala
66+
// Scala 2:
67+
def foo(x: Int)(implicit conv: Int => String): String = x
68+
69+
// Becomes with Scala 3:
70+
def foo(x: Int)(implicit conv: ImplicitConverter[Int, String]): String = x
71+
72+
// Call site is unchanged:
73+
foo(4)(_.toString)
74+
75+
// Scala 2:
76+
implicit val myConverter: Int => String = _.toString
77+
78+
// Becomes with Scala 3:
79+
implicit val myConverter: ImplicitConverter[Int, String] = _.toString
80+
```
81+
82+
Note that implicit conversions are also affected by the [changes to
83+
implicit resolution](implicit-resolution.html) between Scala 2 and
84+
Scala 3.
85+
86+
## Motivation for the changes
87+
88+
The introduction of `ImplicitConverter` in Scala 3 and the decision to
89+
restrict implicit values of this type to be considered as potential
90+
views comes from the desire to remove surprising behavior from the
91+
language:
92+
93+
```scala
94+
implicit val m: Map[Int, String] = Map(1 -> "abc")
95+
96+
val x: String = 1 // scalac: assigns "abc" to x
97+
// Dotty: type error
98+
```
99+
100+
This snippet contains a type error. The right hand side of `val x`
101+
does not conform to type `String`. In Scala 2, the compiler will use
102+
`m` as an implicit conversion from `Int` to `String`, whereas Scala 3
103+
will report a type error, because Map isn't an instance of
104+
`ImplicitConverter`.
105+
106+
## Migration path
107+
108+
Implicit values that are used as views should see their type changed
109+
to `ImplicitConverter`.
110+
111+
For the migration of implicit conversions that are affected by the
112+
changes to implicit resolution, refer to the [Changes in Implicit
113+
Resolution](implicit-resolution.html) for more information.
114+
115+
## Reference
116+
117+
For more information about implicit resolution, see [Changes in
118+
Implicit Resolution](implicit-resolution.html).
119+
Other details are available in
120+
[PR #2065](https://github.com/lampepfl/dotty/pull/2065)
121+
Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,64 @@
11
---
22
layout: doc-page
3-
title: "Restrictions to Implicit Conversions"
3+
title: "Implicit Conversions"
44
---
55

6-
Previously, an implicit value of type `Function1`, or any of its subtypes
7-
could be used as an implicit conversion. That is, the following code would compile
8-
even though it probably masks a type error:
6+
An _implicit conversion_, also called _view_, is a conversion that
7+
is applied by the compiler in several situations:
98

10-
implicit val m: Map[Int, String] = Map(1 -> "abc")
9+
1. When an expression `e` of type `T` is encountered, but the compiler
10+
needs an expression of type `S`.
11+
1. When an expression `e.m` where `e` has type `T` but `T` defines no
12+
member `m` is encountered.
1113

12-
val x: String = 1 // scalac: assigns "abc" to x
13-
// Dotty: type error
14+
In those cases, the compiler looks in the implicit scope for a
15+
conversion that can convert an expression of type `T` to an expression
16+
of type `S` (or to a type that defines a member `m` in the second
17+
case).
1418

15-
By contrast, Dotty only considers _methods_ as implicit conversions, so the
16-
`Map` value `m` above would not qualify as a conversion from `String` to `Int`.
19+
This conversion can be either:
1720

18-
To be able to express implicit conversions passed as parameters, `Dotty`
19-
introduces a new type
21+
1. An `implicit def` of type `T => S` or `(=> T) => S`
22+
1. An implicit value of type `ImplicitConverter[T, S]`
2023

21-
abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
24+
Defining an implicit conversion will emit a warning unless the import
25+
`scala.language.implicitConversions` is in scope, or the flag
26+
`-language:implicitConversions` is given to the compiler.
2227

23-
Implicit values of type `ImplicitConverter[A, B]` do qualify as implicit
24-
conversions. It is as if there was a global implicit conversion method
28+
## Examples
2529

26-
def convert[A, B](x: A)(implicit converter: ImplicitConverter[A, B]): B =
27-
converter(x)
30+
The first example is taken from `scala.Predef`. Thanks to this
31+
implicit conversion, it is possible to pass a `scala.Int` to a Java
32+
method that expects a `java.lang.Integer`
2833

29-
(In reality the Dotty compiler simulates the behavior of this method directly in
30-
its type checking because this turns out to be more efficient).
34+
```scala
35+
import scala.language.implicitConversions
36+
implicit def int2Integer(x: Int): java.lang.Integer =
37+
x.asInstanceOf[java.lang.Integer]
38+
```
3139

32-
In summary, previous code using implicit conversion parameters such as
40+
The second example shows how to use `ImplicitConverter` to define an
41+
`Ordering` for an arbitrary type, given existing `Ordering`s for other
42+
types:
3343

34-
def useConversion(implicit f: A => B) = {
35-
val y: A = ...
36-
val x: B = y // error under Dotty
37-
}
44+
```scala
45+
import scala.language.implicitConversions
46+
implicit def ordT[T, S](
47+
implicit conv: ImplicitConverter[T, S],
48+
ordS: Ordering[S]
49+
): Ordering[T] = {
50+
// `ordS` compares values of type `S`, but we can convert from `T` to `S`
51+
(x: T, y: T) => ordS.compare(x, y)
52+
}
3853

39-
is no longer legal and has to be rewritten to
54+
class A(val x: Int) // The type for which we want an `Ordering`
4055

41-
def useConversion(implicit f: ImplicitConverter[A, B]) = {
42-
val y: A = ...
43-
val x: B = y // OK
44-
}
56+
// Convert `A` to a type for which an `Ordering` is available:
57+
implicit val AToInt: ImplicitConverter[A, Int] = _.x
4558

46-
### Reference
47-
48-
For more info, see [PR #2065](https://github.com/lampepfl/dotty/pull/2065).
59+
implicitly[Ordering[Int]] // Ok, exists in the standard library
60+
implicitly[Ordering[A]] // Ok, will use the implicit conversion from
61+
// `A` to `Int` and the `Ordering` for `Int`.
62+
```
4963

64+
[More details](implicit-conversions-spec.html)

0 commit comments

Comments
 (0)