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