@@ -27,17 +27,17 @@ The Scalafix rule named `ExplicitImplicitTypes` in [ohze/scala-rewrites](https:/
27
27
28
28
Scala 3 does not support implicit conversion from an implicit function value, of the form ` implicit val ev: A => B ` .
29
29
30
- {% tabs scala-3 -implicit_1 %}
31
- {% tab 'Scala 3 Only' %}
30
+ {% tabs scala-2 -implicit_1 %}
31
+ {% tab 'Scala 2 Only' %}
32
32
33
- The following piece of code is now invalid:
33
+ The following piece of code is now invalid in Scala 3 :
34
34
~~~ scala
35
35
trait Pretty {
36
36
val print : String
37
37
}
38
38
39
39
def pretty [A ](a : A )(implicit ev : A => Pretty ): String =
40
- a.print // Error: value print is not a member of A
40
+ a.print // In Scala 3, Error: value print is not a member of A
41
41
~~~
42
42
{% endtab %}
43
43
{% endtabs %}
@@ -47,8 +47,8 @@ The [Scala 3 migration compilation](tooling-migration-mode.html) can warn you ab
47
47
Be aware that this incompatibility can produce a runtime incompatibility and break your program.
48
48
Indeed the compiler can find another implicit conversion from a broader scope, which would eventually cause an undesired behavior at runtime.
49
49
50
- {% tabs scala-3 -implicit_2 %}
51
- {% tab 'Scala 3 Only ' %}
50
+ {% tabs shared -implicit_2 %}
51
+ {% tab 'Scala 2 and 3 ' %}
52
52
53
53
This example illustrates the case:
54
54
~~~ scala
@@ -61,99 +61,90 @@ implicit def anyPretty(any: Any): Pretty = new Pretty { val print = "any" }
61
61
def pretty [A ](a : A )(implicit ev : A => Pretty ): String =
62
62
a.print // always print "any"
63
63
~~~
64
+ {% endtab %}
65
+ {% endtabs %}
64
66
65
67
The resolved conversion depends on the compiler mode:
66
68
- ` -source:3.0-migration ` : the compiler performs the ` ev ` conversion
67
69
- ` -source:3.0 ` : the compiler cannot perform the ` ev ` conversion but it can perform the ` anyPretty ` , which is undesired
68
70
69
- One simple fix is to supply the right conversion explicitly:
71
+ In Scala 3, one simple fix is to supply the right conversion explicitly:
70
72
71
- ~~~ scala
73
+ {% highlight diff %}
72
74
def pretty[ A] (a: A)(implicit ev: A => Pretty): String =
73
- ev(a).print
74
- ~~~
75
- {% endtab %}
76
- {% endtabs %}
75
+ - a.print
76
+ + ev(a).print
77
+ {% endhighlight %}
77
78
78
79
## View Bounds
79
80
80
81
View bounds have been deprecated for a long time but they are still supported in Scala 2.13.
81
82
They cannot be compiled with Scala 3 anymore.
82
83
83
- {% tabs scala-3 -bounds_1 %}
84
- {% tab 'Scala 3 Only' %}
84
+ {% tabs scala-2 -bounds_1 %}
85
+ {% tab 'Scala 2 Only' %}
85
86
~~~ scala
86
87
def foo [A <% Long ](a : A ): Long = a
87
88
~~~
89
+ {% endtab %}
90
+ {% endtabs %}
88
91
89
- In this example we get:
92
+ In this example, in Scala 3, we get this following error message :
90
93
91
- ~~~ text
94
+ {% highlight text %}
92
95
-- Error: src/main/scala/view-bound.scala:2:12
93
96
2 | def foo[ A <% Long] (a: A): Long = a
94
97
| ^
95
98
| view bounds ` <%' are deprecated, use a context bound ` :' instead
96
- ~~~
97
- {% endtab %}
98
- {% endtabs %}
99
+ {% endhighlight %}
99
100
100
101
The message suggests to use a context bound instead of a view bound but it would change the signature of the method.
101
102
It is probably easier and safer to preserve the binary compatibility.
102
103
To do so the implicit conversion must be declared and called explicitly.
103
104
104
105
Be careful not to fall in the runtime incompatibility described above, in [ Implicit Views] ( #implicit-views ) .
105
106
106
- {% tabs runtime_1 class=tabs-scala-version %}
107
- {% tab 'Scala 2' for=runtime_1 %}
108
- ~~~ scala
109
- def foo [A <% Long ](a : A ): Long = a
110
- ~~~
111
-
112
- {% endtab %}
113
- {% tab 'Scala 3' for=runtime_1 %}
114
- ~~~ scala
115
- def foo [A ](a : A )(implicit ev : A => Long ): Long = ev(a)
116
- ~~~
117
- {% endtab %}
118
- {% endtabs %}
107
+ {% highlight diff %}
108
+ -def foo[ A <% Long] (a: A): Long = a
109
+ +def foo[ A] (a: A)(implicit ev: A => Long): Long = ev(a)
110
+ {% endhighlight %}
119
111
120
112
## Ambiguous Conversion On ` A ` And ` => A `
121
113
122
114
In Scala 2.13 the implicit conversion on ` A ` wins over the implicit conversion on ` => A ` .
123
115
It is not the case in Scala 3 anymore, and leads to an ambiguous conversion.
124
116
125
- {% tabs ambiguous_1 class=tabs-scala-version %}
126
- {% tab 'Scala 2' for=ambiguous_1 %}
127
-
128
117
For instance, in this example:
118
+
119
+ {% tabs scala-2-ambiguous_1 %}
120
+ {% tab 'Scala 2 Only' %}
129
121
~~~ scala
130
122
implicit def boolFoo (bool : Boolean ): Foo = ???
131
123
implicit def lazyBoolFoo (lazyBool : => Boolean ): Foo = ???
132
124
133
125
true .foo()
134
126
~~~
127
+ {% endtab %}
128
+ {% endtabs %}
135
129
136
130
The Scala 2.13 compiler chooses the ` boolFoo ` conversion but the Scala 3 compiler fails to compile.
137
131
138
- ~~~ text
132
+ {% highlight text %}
139
133
-- Error: src/main/scala/ambiguous-conversion.scala:4:19
140
134
9 | true.foo()
141
135
| ^^^^
142
136
|Found: (true : Boolean)
143
137
|Required: ?{ foo: ? }
144
138
|Note that implicit extension methods cannot be applied because they are ambiguous;
145
139
|both method boolFoo in object Foo and method lazyBoolFoo in object Foo provide an extension method ` foo ` on (true : Boolean)
146
- ~~~
147
- {% endtab %}
148
- {% tab 'Scala 3' for=ambiguous_1 %}
140
+ {% endhighlight %}
149
141
150
142
A temporary solution is to write the conversion explicitly.
151
143
152
- ~~~ scala
144
+ {% highlight diff %}
153
145
implicit def boolFoo(bool: Boolean): Foo = ???
154
146
implicit def lazyBoolFoo(lazyBool: => Boolean): Foo = ???
155
147
156
- boolFoo(true ).foo()
157
- ~~~
158
- {% endtab %}
159
- {% endtabs %}
148
+ -true.foo()
149
+ +boolFoo(true).foo()
150
+ {% endhighlight %}
0 commit comments