Skip to content

Commit e3d594a

Browse files
committed
Add code tabs to contextual abstractions page
1 parent e909502 commit e3d594a

File tree

1 file changed

+52
-27
lines changed

1 file changed

+52
-27
lines changed

_overviews/scala3-migration/incompat-contextual-abstractions.md

+52-27
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,31 @@ The Scalafix rule named `ExplicitImplicitTypes` in [ohze/scala-rewrites](https:/
2727

2828
Scala 3 does not support implicit conversion from an implicit function value, of the form `implicit val ev: A => B`.
2929

30-
The following piece of code is now invalid:
30+
{% tabs scala-3-implicit_1 %}
31+
{% tab 'Scala 3 Only' %}
3132

32-
```scala
33+
The following piece of code is now invalid:
34+
~~~ scala
3335
trait Pretty {
3436
val print: String
3537
}
3638

3739
def pretty[A](a: A)(implicit ev: A => Pretty): String =
3840
a.print // Error: value print is not a member of A
39-
```
41+
~~~
42+
{% endtab %}
43+
{% endtabs %}
4044

4145
The [Scala 3 migration compilation](tooling-migration-mode.html) can warn you about those cases, but it does not try to fix it.
4246

4347
Be aware that this incompatibility can produce a runtime incompatibility and break your program.
4448
Indeed the compiler can find another implicit conversion from a broader scope, which would eventually cause an undesired behavior at runtime.
4549

46-
This example illustrates the case:
50+
{% tabs scala-3-implicit_2 %}
51+
{% tab 'Scala 3 Only' %}
4752

48-
```scala
53+
This example illustrates the case:
54+
~~~ scala
4955
trait Pretty {
5056
val print: String
5157
}
@@ -54,81 +60,100 @@ implicit def anyPretty(any: Any): Pretty = new Pretty { val print = "any" }
5460

5561
def pretty[A](a: A)(implicit ev: A => Pretty): String =
5662
a.print // always print "any"
57-
```
63+
~~~
5864

5965
The resolved conversion depends on the compiler mode:
6066
- `-source:3.0-migration`: the compiler performs the `ev` conversion
6167
- `-source:3.0`: the compiler cannot perform the `ev` conversion but it can perform the `anyPretty`, which is undesired
6268

6369
One simple fix is to supply the right conversion explicitly:
6470

65-
{% highlight diff %}
71+
~~~ scala
6672
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 %}
7077

7178
## View Bounds
7279

7380
View bounds have been deprecated for a long time but they are still supported in Scala 2.13.
7481
They cannot be compiled with Scala 3 anymore.
7582

76-
```scala
83+
{% tabs scala-3-bounds_1 %}
84+
{% tab 'Scala 3 Only' %}
85+
~~~ scala
7786
def foo[A <% Long](a: A): Long = a
78-
```
87+
~~~
7988

8089
In this example we get:
8190

82-
{% highlight text %}
91+
~~~ text
8392
-- Error: src/main/scala/view-bound.scala:2:12
8493
2 | def foo[A <% Long](a: A): Long = a
8594
| ^
8695
| view bounds `<%' are deprecated, use a context bound `:' instead
87-
{% endhighlight %}
96+
~~~
97+
{% endtab %}
98+
{% endtabs %}
8899

89100
The message suggests to use a context bound instead of a view bound but it would change the signature of the method.
90101
It is probably easier and safer to preserve the binary compatibility.
91102
To do so the implicit conversion must be declared and called explicitly.
92103

93104
Be careful not to fall in the runtime incompatibility described above, in [Implicit Views](#implicit-views).
94105

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 %}
99119

100120
## Ambiguous Conversion On `A` And `=> A`
101121

102122
In Scala 2.13 the implicit conversion on `A` wins over the implicit conversion on `=> A`.
103123
It is not the case in Scala 3 anymore, and leads to an ambiguous conversion.
104124

105-
For instance, in this example:
125+
{% tabs ambiguous_1 class=tabs-scala-version %}
126+
{% tab 'Scala 2' for=ambiguous_1 %}
106127

107-
```scala
128+
For instance, in this example:
129+
~~~ scala
108130
implicit def boolFoo(bool: Boolean): Foo = ???
109131
implicit def lazyBoolFoo(lazyBool: => Boolean): Foo = ???
110132

111133
true.foo()
112-
```
134+
~~~
113135

114136
The Scala 2.13 compiler chooses the `boolFoo` conversion but the Scala 3 compiler fails to compile.
115137

116-
{% highlight text %}
138+
~~~ text
117139
-- Error: src/main/scala/ambiguous-conversion.scala:4:19
118140
9 | true.foo()
119141
| ^^^^
120142
|Found: (true : Boolean)
121143
|Required: ?{ foo: ? }
122144
|Note that implicit extension methods cannot be applied because they are ambiguous;
123145
|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 %}
125149

126150
A temporary solution is to write the conversion explicitly.
127151

128-
{% highlight diff %}
152+
~~~ scala
129153
implicit def boolFoo(bool: Boolean): Foo = ???
130154
implicit def lazyBoolFoo(lazyBool: => Boolean): Foo = ???
131155

132-
-true.foo()
133-
+boolFoo(true).foo()
134-
{% endhighlight %}
156+
boolFoo(true).foo()
157+
~~~
158+
{% endtab %}
159+
{% endtabs %}

0 commit comments

Comments
 (0)