Skip to content

Commit c8d5bc2

Browse files
committed
Summarise blog at start and update version numbers
1 parent 0cb14cd commit c8d5bc2

File tree

1 file changed

+114
-107
lines changed

1 file changed

+114
-107
lines changed

blog/_posts/2020-10-12-scala-3-forward-compat.md renamed to blog/_posts/2020-11-10-scala-3-forward-compat.md

Lines changed: 114 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ Scala 3 is coming, with a release candidate slated for the end of 2020.
99
With that knowledge comes the inevitable question:
1010
should I migrate, and what is the potential cost?
1111

12-
With this blog we would like to outline some scenarios for maintainers of projects who
13-
want to move to Scala 3, and how these
14-
scenarios are made simpler by the ability to read Scala 3 compiled dependencies
15-
from Scala 2.13. There is also a [troubleshooting](#troubleshooting) section
16-
in case you encounter issues following the steps described in the scenarios.
12+
For maintainers of projects, the migration process may become easier
13+
with the release of Scala 2.13.4, which comes with a new feature:
14+
reading and compiling against Scala 3 dependencies.
1715

1816
>As a quick aside, If you would like to know how it is possible to mix
1917
>dependencies between the two compilers, watch the talk
@@ -24,17 +22,42 @@ in case you encounter issues following the steps described in the scenarios.
2422
>which terms, types and implicits are defined in a given dependency,
2523
>and what code needs to be generated to use them correctly.
2624
25+
## Overview
26+
27+
With a guided tutorial, we will show you how to approach the following scenarios:
28+
1. You have an application consisting of many subprojects that may depend on
29+
each other, built with Scala 2.13, and would like to transition each one
30+
independently to Scala 3.0
31+
2. You have an application, built with Scala 2.13, and want to use some
32+
new features of a library that has migrated to Scala 3.0
33+
34+
We will show you by example how Scala 2.13.4 makes these scenarios more simple:
35+
We will take a small multi-module project of two sub-projects, a `shared` module,
36+
containing simple data structures, and an `app` module that depends on `shared`.
37+
We will in turn migrate each sub-project to Scala 3, and show that it does not
38+
matter in which order you migrate the projects, as `app` will continue
39+
to build and run.
40+
41+
A reader applying the steps in the tutorial to their own project should note that not
42+
all features of Scala 3 are forward compatibile with Scala 2, such as `inline` methods.
43+
Consequently, we recommend that the user limits their usage of Scala 3 exclusive
44+
features when migrating incrementally. More information is provided in the
45+
[forward compatibility](#forward-compatibility) section.
46+
47+
In addition, we provide a [troubleshooting](#troubleshooting) section for the reader,
48+
which aims to suggest steps to take when applying this guide to their own projects
49+
and a problem occurs.
50+
2751
## Scenarios
2852

29-
1. You have an application consisting of many subprojects that may depend on
30-
each other, built with Scala 2.13, and would like to transition each one
31-
independently to Scala 3.0
32-
2. You have an application, built with Scala 2.13, and want to use some
33-
new features of a library that has migrated to Scala 3.0
53+
The [overview](#overview) section describes two scenarios that we will guide you through
54+
in this blog:
55+
- Migrating multi-module projects incrementally from Scala 2 to Scala 3
56+
- Taking advantage of new features in a library that is published for Scala 3.
3457

35-
Let's take a look at each scenario in turn:
58+
Let's look at each scenario in turn:
3659

37-
## Migrating a multi module project
60+
### 1. Migrate a Multi-Module Project in Any Order
3861

3962
If you want to migrate a multi module project to Scala 3,
4063
it does not matter in which order you migrate the modules, they will be
@@ -47,19 +70,15 @@ which has some common domain model data structures, and `app`,
4770
which uses those data structures.
4871

4972
For this project, we will pick
50-
[sbt 1.4.0](https://github.com/sbt/sbt/releases/tag/v1.4.0),
73+
[sbt 1.4.2](https://github.com/sbt/sbt/releases/tag/v1.4.2),
5174
this allows you to mix projects of different Scala versions with very
5275
little extra effort (thanks to Eugene Yokota).
5376

54-
This tutorial also assumes the release of Scala `2.13.4`, but if you would
55-
like to follow along today, you can [modify the build file](#early-access)
56-
to use an early access snapshot release.
57-
5877
To begin, our project looks like the following:
5978

6079
```scala
6180
// project/build.properties
62-
sbt.version=1.4.0
81+
sbt.version=1.4.2
6382
```
6483

6584
```scala
@@ -106,16 +125,15 @@ Tiger
106125
```
107126

108127
At this point we can try something exciting, compile either subproject with
109-
Scala 3 and see if it works. At the time of writing, the Scala version
110-
`0.27.0-RC1` is the latest preview release of Scala 3.
128+
Scala 3 and see if it works.
111129

112130
First, add the dotty plugin (this helps with managing and inspecting the
113131
`scalaVersion` setting with Scala 3)
114132

115133
```scala
116134
// project/plugins.sbt
117135

118-
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.4.2")
136+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.4.5")
119137
```
120138

121139
Then we can change the `scalaVersion` of `app`:
@@ -129,7 +147,7 @@ Then we can change the `scalaVersion` of `app`:
129147

130148
lazy val app = project
131149
.dependsOn(shared)
132-
+ .settings(scalaVersion := "0.27.0-RC1")
150+
+ .settings(scalaVersion := "3.0.0-M1")
133151
{% endhighlight %}
134152

135153
To recap, `app` is now compiled by Scala 3 and depends on `shared`,
@@ -138,19 +156,21 @@ which is compiled by Scala 2.13.
138156
If we do `sbt app/run` we should see the `app` project recompile
139157
and it will run as before.
140158

141-
Now lets try the other way around:
159+
Now lets try the other way around, in this case we also have to enable
160+
reading Scala 3 dependencies in Scala 2 with the flag `-Ytasty-reader`:
142161

143162
{% highlight diff %}
144163
// build.sbt
145164

146165
ThisBuild / scalaVersion := "2.13.4"
147166

148167
lazy val shared = project
149-
+ .settings(scalaVersion := "0.27.0-RC1")
168+
+ .settings(scalaVersion := "3.0.0-M1")
150169

151170
lazy val app = project
152171
.dependsOn(shared)
153-
- .settings(scalaVersion := "0.27.0-RC1")
172+
- .settings(scalaVersion := "3.0.0-M1")
173+
+ .settings(scalacOptions += "-Ytasty-reader")
154174
{% endhighlight %}
155175

156176
Here we have the opposite, `app` is compiled by Scala 2.13 and depends
@@ -159,8 +179,8 @@ the command `sbt 'show app/dependencyTree'`, which outputs the following:
159179

160180
```
161181
app:app_2.13:0.1.0-SNAPSHOT [S]
162-
+-shared:shared_0.27:0.1.0-SNAPSHOT
163-
+-ch.epfl.lamp:dotty-library_0.27:0.27.0-RC1 [S]
182+
+-shared:shared_3.0.0-M1:0.1.0-SNAPSHOT
183+
+-org.scala-lang:scala3-library_3.0.0-M1:3.0.0-M1 [S]
164184
```
165185

166186
If we then try `sbt app/run` both `shared` and `app` subprojects will recompile
@@ -172,7 +192,11 @@ To summarise this part, we have shown that it is possible to migrate subprojects
172192
to Scala 3 from Scala 2.13 in any order, gradually, and continue to build and
173193
run them if they mix versions.
174194

175-
## Using a Scala 3 Library Dependency
195+
### 2. Using a Scala 3 Library Dependency
196+
197+
> TODO: there is currently no library published for Scala `3.0.0-M1`
198+
> we can either rewrite this section to just explain it in principle,
199+
> with a fake library or wait for some library to be available.
176200
177201
In the section above, we have seen how it is possible for a subproject compiled
178202
with Scala 2.13 to depend on a subproject compiled with Scala 3. The same
@@ -187,12 +211,12 @@ on the fansi library to `app` and try to change our output to be colored blue:
187211
ThisBuild / scalaVersion := "2.13.4"
188212

189213
lazy val shared = project
190-
.settings(scalaVersion := "0.27.0-RC1")
214+
.settings(scalaVersion := "3.0.0-M1")
191215

192216
lazy val app = project
193217
.dependsOn(shared)
194218
+ .settings(
195-
+ libraryDependencies += "com.lihaoyi" % "fansi_0.27" % "0.2.9"
219+
+ libraryDependencies += "com.lihaoyi" % "fansi_3.0.0-M1" % "0.2.9" // TODO fansi has not been published for `3.0.0-M1`
196220
+ )
197221
{% endhighlight %}
198222

@@ -201,11 +225,11 @@ we take a standard module id and change it as so:
201225

202226
{% highlight diff %}
203227
-"com.lihaoyi" %% "fansi" % "0.2.9"
204-
+"com.lihaoyi" % "fansi_0.27" % "0.2.9"
228+
+"com.lihaoyi" % "fansi_3.0.0-M1" % "0.2.9"
205229
{% endhighlight %}
206230

207231
By replacing `%%` with `%`, we can then manually specify the binary version,
208-
leading us to add `_0.27` to the name of the module.
232+
leading us to add `_3.0.0-M1` to the name of the module.
209233

210234
We now update `Main.scala` to use the fansi library:
211235

@@ -227,18 +251,72 @@ If we then run again `sbt 'show app/dependencyTree'` we see the following:
227251

228252
```
229253
app:app_2.13:0.1.0-SNAPSHOT [S]
230-
+-com.lihaoyi:fansi_0.27:0.2.9
231-
| +-com.lihaoyi:sourcecode_0.27:0.2.1
254+
+-com.lihaoyi:fansi_3.0.0-M1:0.2.9
255+
| +-com.lihaoyi:sourcecode_3.0.0-M1:0.2.1
232256
|
233-
+-shared:shared_0.27:0.1.0-SNAPSHOT
234-
+-ch.epfl.lamp:dotty-library_0.27:0.27.0-RC1 [S]
257+
+-shared:shared_3.0.0-M1:0.1.0-SNAPSHOT
258+
+-org.scala-lang:scala3-library_3.0.0-M1:3.0.0-M1 [S]
235259
```
236260

237261
To summarise, in this section we have shown how it is possible to use a
238262
third party library dependency, compiled with Scala 3, from Scala 2.13.
239263
If there are issues with changing the binary version of a particular
240264
dependency you have, check out the [troubleshooting](#troubleshooting) section.
241265

266+
## Forward Compatibility
267+
When migrating a subproject to Scala 3, where downstream consuming subprojects are likely to be on Scala 2.13, we would recommend restricting the usage of new features in Scala 3. This will maximise compatibility as you migrate each subproject. However, it is possible to start using some features of Scala 3 without issue, this is due to a limited forward compatibility in Scala 2.13 with some new Scala 3 features.
268+
269+
Forward compatibility means that many definitions created by using new Scala 3 features
270+
can be used from Scala 2.13, however they will be remapped to features
271+
that exist in Scala 2.13. For example,
272+
[extension methods](http://dotty.epfl.ch/docs/reference/contextual/extension-methods.html)
273+
can only be used as ordinary methods. So for cross-compatible code we recommend
274+
to continue using implicit classes to encode extension methods.
275+
276+
On the other hand, some features of Scala 3 are not mappable to features in Scala 2.13,
277+
and will cause a compile-time error when using them. A longer list can be seen in the
278+
migration guide, describing [how Scala 2 reacts to different Scala 3 features](https://scalacenter.github.io/scala-3-migration-guide/docs/compatibility.html#the-scala-2-tasty-reader).
279+
280+
For unsupported features, a best effort is made
281+
to report errors at the use-site that is problematic. For example,
282+
[match types](http://dotty.epfl.ch/docs/reference/new-types/match-types.html)
283+
are not supported. If we define in the `shared` project the type `Elem`:
284+
285+
```scala
286+
// shared/src/main/scala/example/MatchTypes.scala
287+
package example
288+
289+
object MatchTypes {
290+
type Elem[X] = X match {
291+
case List[t] => t
292+
case Array[t] => t
293+
}
294+
}
295+
```
296+
297+
and then try to use it in the `app` project:
298+
299+
```scala
300+
// app/src/main/scala/example/TestMatchTypes.scala
301+
package example
302+
303+
object TestMatchTypes {
304+
def test: MatchTypes.Elem[List[String]] = "hello"
305+
}
306+
```
307+
308+
we get the following error when calling `sbt app/run`:
309+
310+
```
311+
[error] TestMatchTypes.scala:5:25: Unsupported Scala 3 match type in bounds of type Elem; found in object example.MatchTypes.
312+
[error] def test: MatchTypes.Elem[List[String]] = "hello"
313+
[error] ^
314+
[error] one error found
315+
```
316+
317+
The error is standard for all unsupported Scala 3 features, naming the feature,
318+
the location of the definition and the location where it is used.
319+
242320
## Troubleshooting
243321
There are some situations where the steps described in the sections above do
244322
not work out of the box for your own project, and some other considerations
@@ -295,59 +373,6 @@ compilation, or disrupts runtime behaviour, please consider the above
295373
troubleshooting topics, but instead assume that `A` is the third party
296374
library, and `B` is your Scala 2.13 project that depends on it.
297375

298-
## Forward Compatibility
299-
When migrating a subproject to Scala 3, where downstream consuming subprojects are likely to be on Scala 2.13, we would recommend restricting the usage of new features in Scala 3. This will maximise compatibility as you migrate each subproject. However, it is possible to start using some features of Scala 3 without issue, this is due to a limited forward compatibility in Scala 2.13 with some new Scala 3 features.
300-
301-
Forward compatibility means that many definitions created by using new Scala 3 features
302-
can be used from Scala 2.13, however they will be remapped to features
303-
that exist in Scala 2.13. For example,
304-
[extension methods](http://dotty.epfl.ch/docs/reference/contextual/extension-methods.html)
305-
can only be used as ordinary methods, using their expanded name.
306-
307-
On the other hand, some features of Scala 3 are not mappable to features in Scala 2.13,
308-
and will cause a compile-time error when using them. A longer list can be seen in the
309-
migration guide, describing [how Scala 2 reacts to different Scala 3 features](https://scalacenter.github.io/scala-3-migration-guide/docs/compatibility.html#the-scala-2-tasty-reader).
310-
311-
For unsupported features, a best effort is made
312-
to report errors at the use-site that is problematic. For example,
313-
[match types](http://dotty.epfl.ch/docs/reference/new-types/match-types.html)
314-
are not supported. If we define in the `shared` project the type `Elem`:
315-
316-
```scala
317-
// shared/src/main/scala/example/MatchTypes.scala
318-
package example
319-
320-
object MatchTypes {
321-
type Elem[X] = X match {
322-
case List[t] => t
323-
case Array[t] => t
324-
}
325-
}
326-
```
327-
328-
and then try to use it in the `app` project:
329-
330-
```scala
331-
// app/src/main/scala/example/TestMatchTypes.scala
332-
package example
333-
334-
object TestMatchTypes {
335-
def test: MatchTypes.Elem[List[String]] = "hello"
336-
}
337-
```
338-
339-
we get the following error when calling `sbt app/run`:
340-
341-
```
342-
[error] TestMatchTypes.scala:5:25: Unsupported Scala 3 match type in bounds of type Elem; found in object example.MatchTypes.
343-
[error] def test: MatchTypes.Elem[List[String]] = "hello"
344-
[error] ^
345-
[error] one error found
346-
```
347-
348-
The error is standard for all unsupported Scala 3 features, naming the feature,
349-
the location of the definition and the location where it is used.
350-
351376
## Contributing
352377

353378
If you have found an issue with Scala 3 dependencies in Scala 2.13 and want to try
@@ -362,24 +387,6 @@ project to Scala 3 while continuing to use all the parts together during the
362387
transition. We encourage you to try out this process and let us know of any
363388
issues with using Scala 3 dependencies from Scala 2.13.
364389

365-
## Early Access
366-
367-
If you would like to try out Scala 3 dependencies early, you can use a
368-
snapshot version of Scala 2.13.4 by adding the Scala integration resolver
369-
to your build:
370-
371-
{% highlight diff %}
372-
// build.sbt
373-
374-
-ThisBuild / scalaVersion := "2.13.4"
375-
+ThisBuild / scalaVersion := "2.13.4-bin-8891679"
376-
377-
+Global / resolvers += "scala-integration".at(
378-
+ "https://scala-ci.typesafe.com/artifactory/scala-integration/")
379-
380-
...
381-
{% endhighlight %}
382-
383390
## Important Links
384391

385392
- [Scala 3 Migration Guide](https://scalacenter.github.io/scala-3-migration-guide/)

0 commit comments

Comments
 (0)