Skip to content

Commit 377ca80

Browse files
bishaboshasjrd
andauthored
finish migration to code tabs in migration guide (#3035)
* finish migration to code tabs in migration guide * Update _overviews/scala3-migration/incompat-type-inference.md Co-authored-by: Sébastien Doeraene <[email protected]> --------- Co-authored-by: Sébastien Doeraene <[email protected]>
1 parent 65ac91c commit 377ca80

File tree

4 files changed

+85
-26
lines changed

4 files changed

+85
-26
lines changed

_overviews/scala3-migration/incompat-type-inference.md

+30-17
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,15 @@ The new algorithm is better than the old one, but sometime it can fail where Sca
1414

1515
> It is always good practice to write the result types of all public values and methods explicitly.
1616
> It prevents the public API of your library from changing with the Scala version, because of different inferred types.
17-
>
17+
>
1818
> This can be done prior to the Scala 3 migration by using the [ExplicitResultTypes](https://scalacenter.github.io/scalafix/docs/rules/ExplicitResultTypes.html) rule in Scalafix.
1919
2020
## Return Type of an Override Method
2121

2222
In Scala 3 the return type of an override method is inferred by inheritance from the base method, whereas in Scala 2.13 it is inferred from the left hand side of the override method.
2323

24-
```scala
25-
class Parent {
26-
def foo: Foo = new Foo
27-
}
28-
29-
class Child extends Parent {
30-
override def foo = new RichFoo(super.foo)
31-
}
32-
```
33-
34-
In this example, `Child#foo` returns a `RichFoo` in Scala 2.13 but a `Foo` in Scala 3.
35-
It can lead to compiler errors as demonstrated below.
36-
24+
{% tabs define_parent_child %}
25+
{% tab 'Scala 2 and 3' %}
3726
```scala
3827
class Foo
3928

@@ -48,13 +37,24 @@ class Parent {
4837
class Child extends Parent {
4938
override def foo = new RichFoo(super.foo)
5039
}
40+
```
41+
{% endtab %}
42+
{% endtabs %}
43+
44+
In this example, `Child#foo` returns a `RichFoo` in Scala 2.13 but a `Foo` in Scala 3.
45+
It can lead to compiler errors as demonstrated below.
5146

47+
{% tabs extend_parent_child %}
48+
{% tab 'Scala 3 Only' %}
49+
```scala
5250
(new Child).foo.show // Scala 3 error: value show is not a member of Foo
5351
```
52+
{% endtab %}
53+
{% endtabs %}
5454

5555
In some rare cases involving implicit conversions and runtime casting it could even cause a runtime failure.
5656

57-
The solution is to make the return type of the override method explicit:
57+
The solution is to make the return type of the override method explicit so that it matches what is inferred in 2.13:
5858

5959
{% highlight diff %}
6060
class Child extends Parent {
@@ -68,19 +68,32 @@ class Child extends Parent {
6868
Scala 2 reflective calls are dropped and replaced by the broader [Programmatic Structural Types]({{ site.scala3ref }}/changed-features/structural-types.html).
6969

7070
Scala 3 can imitate Scala 2 reflective calls by making `scala.reflect.Selectable.reflectiveSelectable` available wherever `scala.language.reflectiveCalls` is imported.
71-
However the Scala 3 compiler does not infer structural types by default, and thus fails at compiling:
7271

72+
{% tabs define_structural %}
73+
{% tab 'Scala 2 and 3' %}
7374
```scala
7475
import scala.language.reflectiveCalls
7576

7677
val foo = new {
7778
def bar: Unit = ???
7879
}
80+
```
81+
{% endtab %}
82+
{% endtabs %}
7983

84+
However the Scala 3 compiler does not infer structural types by default.
85+
It infers the type `Object` for `foo` instead of `{ def bar: Unit }`.
86+
Therefore, the following structural selection fails to compile:
87+
88+
{% tabs use_structural %}
89+
{% tab 'Scala 3 Only' %}
90+
```scala
8091
foo.bar // Error: value bar is not a member of Object
8192
```
93+
{% endtab %}
94+
{% endtabs %}
8295

83-
The straightforward solution is to write down the structural type.
96+
The straightforward solution is to explicitly write down the structural type.
8497

8598
{% highlight diff %}
8699
import scala.language.reflectiveCalls

_overviews/scala3-migration/plugin-kind-projector.md

+50-4
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ next-page: external-resources
99

1010
In the future, Scala 3 will use the `_` underscore symbol for placeholders in type lambdas---just as the underscore is currently used for placeholders in (ordinary) term-level lambdas.
1111

12-
The new type lambda syntax is not enabled by default, to enable it, use a compiler flag `-Ykind-projector:underscores`. Note that enabling underscore type lambdas will disable usage of `_` as a wildcard, you will only be able to write wildcards using the `?` symbol.
12+
The new type lambda syntax is not enabled by default, to enable it, use a compiler flag `-Ykind-projector:underscores`. Note that enabling underscore type lambdas will disable usage of `_` as a wildcard, you will only be able to write wildcards using the `?` symbol.
1313

1414
If you wish to cross-compile a project for Scala 2 & Scala 3 while using underscore type lambdas for both, you may do so starting with [kind-projector](https://github.com/typelevel/kind-projector) version `0.13.0` and up and Scala 2 versions `2.13.6` and `2.12.14`.
1515
To enable it, add the compiler flags `-Xsource:3 -P:kind-projector:underscore-placeholders` to your build.
16-
As in Scala 3, this will disable usage of `_` as a wildcard, however, the flag `-Xsource:3` will allow you to replace it with the `?` symbol.
16+
As in Scala 3, this will disable usage of `_` as a wildcard, however, the flag `-Xsource:3` will allow you to replace it with the `?` symbol.
1717

1818
The following `sbt` configuration will set up the correct flags to cross-compile with new syntax:
1919

@@ -34,43 +34,65 @@ In turn, you will also have to rewrite all usages of `_` as the wildcard to use
3434

3535
For example the following usage of the wildcard:
3636

37+
{% tabs wildcard_scala2 %}
38+
{% tab 'Scala 2 Only' %}
3739
```scala
38-
def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = widgets.find(_.name == name)
40+
def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] =
41+
widgets.find(_.name == name)
3942
```
43+
{% endtab %}
44+
{% endtabs %}
4045

4146
Must be rewritten to:
4247

48+
{% tabs wildcard_scala3 %}
49+
{% tab 'Scala 3 Only' %}
4350
```scala
44-
def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = widgets.find(_.name == name)
51+
def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] =
52+
widgets.find(_.name == name)
4553
```
54+
{% endtab %}
55+
{% endtabs %}
4656

4757
And the following usages of kind-projector's `*` placeholder:
4858

59+
{% tabs kind_projector_scala2 %}
60+
{% tab 'Scala 2 Only' %}
4961
```scala
5062
Tuple2[*, Double] // equivalent to: type R[A] = Tuple2[A, Double]
5163
Either[Int, +*] // equivalent to: type R[+A] = Either[Int, A]
5264
Function2[-*, Long, +*] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
5365
```
66+
{% endtab %}
67+
{% endtabs %}
5468

5569
Must be rewritten to:
5670

71+
{% tabs kind_projector_scala3 %}
72+
{% tab 'Scala 3 Only' %}
5773
```scala
5874
Tuple2[_, Double] // equivalent to: type R[A] = Tuple2[A, Double]
5975
Either[Int, +_] // equivalent to: type R[+A] = Either[Int, A]
6076
Function2[-_, Long, +_] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
6177
```
78+
{% endtab %}
79+
{% endtabs %}
6280

6381
## Compiling Existing Code
6482

6583
Even without migrating to underscore type lambdas, you will likely be able to compile most of it with Scala 3 without changes.
6684

6785
Use the flag `-Ykind-projector` to enable support for `*`-based type lambdas (without enabling underscore type lambdas), the following forms will now compile:
6886

87+
{% tabs kind_projector_cross %}
88+
{% tab 'Scala 2 and 3' %}
6989
```scala
7090
Tuple2[*, Double] // equivalent to: type R[A] = Tuple2[A, Double]
7191
Either[Int, +*] // equivalent to: type R[+A] = Either[Int, A]
7292
Function2[-*, Long, +*] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
7393
```
94+
{% endtab %}
95+
{% endtabs %}
7496

7597
## Rewriting Incompatible Constructs
7698

@@ -82,6 +104,8 @@ Scala 3's `-Ykind-projector` & `-Ykind-projector:underscores` implement only a s
82104

83105
You must rewrite ALL of the following forms:
84106

107+
{% tabs kind_projector_illegal_scala2 %}
108+
{% tab 'Scala 2 Only' %}
85109
```scala
86110
// classic
87111
EitherT[*[_], Int, *] // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
@@ -92,36 +116,58 @@ EitherT[_[_], Int, _] // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
92116
// named Lambda
93117
Lambda[(F[_], A) => EitherT[F, Int, A]]
94118
```
119+
{% endtab %}
120+
{% endtabs %}
95121

96122
Into the following long-form to cross-compile with Scala 3:
97123

124+
{% tabs kind_projector_illegal_cross %}
125+
{% tab 'Scala 2 and 3' %}
98126
```scala
99127
type MyLambda[F[_], A] = EitherT[F, Int, A]
100128
MyLambda
101129
```
130+
{% endtab %}
131+
{% endtabs %}
102132

103133
Alternatively you may use Scala 3's [Native Type Lambdas]({{ site.scala3ref }}/new-types/type-lambdas.html) if you do not need to cross-compile:
104134

135+
{% tabs kind_projector_illegal_scala3 %}
136+
{% tab 'Scala 3 Only' %}
105137
```scala
106138
[F[_], A] =>> EitherT[F, Int, A]
107139
```
140+
{% endtab %}
141+
{% endtabs %}
108142

109143
For `Lambda` you must rewrite the following form:
110144

145+
{% tabs kind_projector_illegal_lambda_scala2 %}
146+
{% tab 'Scala 2 Only' %}
111147
```scala
112148
Lambda[(`+E`, `+A`) => Either[E, A]]
113149
```
150+
{% endtab %}
151+
{% endtabs %}
114152

115153
To the following to cross-compile:
116154

155+
{% tabs kind_projector_illegal_lambda_cross %}
156+
{% tab 'Scala 2 and 3' %}
117157
```scala
118158
λ[(`+E`, `+A`) => Either[E, A]]
119159
```
160+
{% endtab %}
161+
{% endtabs %}
120162

121163
Or alternatively to Scala 3 type lambdas:
122164

165+
{% tabs kind_projector_illegal_lambda_scala3 %}
166+
{% tab 'Scala 3 Only' %}
123167
```scala
124168
[E, A] =>> Either[E, A]
125169
```
170+
{% endtab %}
171+
{% endtabs %}
126172

127173
Note: Scala 3 type lambdas no longer need `-` or `+` variance markers on parameters, these are now inferred.

_overviews/scala3-migration/tutorial-macro-cross-building.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ If you try to compile with Scala 3 you should see some errors of the same kind a
101101
{% highlight text %}
102102
sbt:example> ++3.3.1
103103
sbt:example> example / compile
104-
[error] -- Error: /example/src/main/scala/location/Location.scala:15:35
104+
[error] -- Error: /example/src/main/scala/location/Location.scala:15:35
105105
[error] 15 | val location = typeOf[Location]
106106
[error] | ^
107107
[error] | No TypeTag available for location.Location
108-
[error] -- Error: /example/src/main/scala/location/Location.scala:18:4
108+
[error] -- Error: /example/src/main/scala/location/Location.scala:18:4
109109
[error] 18 | q"new $location($path, $line)"
110110
[error] | ^
111111
[error] |Scala 2 macro cannot be used in Dotty. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html
@@ -165,7 +165,7 @@ They must have the exact same signature than their Scala 2.13 counterparts.
165165
package location
166166

167167
object Macros:
168-
def location: Location = ???
168+
inline def location: Location = ???
169169
```
170170
{% endtab %}
171171
{% endtabs %}
@@ -191,7 +191,7 @@ object Macros:
191191
private def locationImpl(using quotes: Quotes): Expr[Location] =
192192
import quotes.reflect.Position
193193
val pos = Position.ofMacroExpansion
194-
val file = Expr(pos.sourceFile.jpath.toString)
194+
val file = Expr(pos.sourceFile.path.toString)
195195
val line = Expr(pos.startLine + 1)
196196
'{new Location($file, $line)}
197197
```

_overviews/scala3-migration/tutorial-macro-mixing.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ previous-page: tutorial-macro-mixing
77
next-page: tooling-syntax-rewriting
88
---
99

10-
This tutorial shows how to mix Scala 2.13 and Scala 3 macros in a single artifact. This means that consumers can use '-Ytasty-reader' from Scala 2.13 code that uses your macros.
10+
This tutorial shows how to mix Scala 2.13 and Scala 3 macros in a single artifact. This means that consumers can use `-Ytasty-reader` from Scala 2.13 code that uses your macros.
1111

1212
There are two main benefits of this:
1313

0 commit comments

Comments
 (0)