Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document migration to Scala 3 Underscore Type Lambdas syntax and cross-compiling with kind-projector #203

Merged
merged 1 commit into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/compiler-options/compiler-options-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,4 @@ You can enable and configure them with some new native options.
| 2.13.x | 3.0.x |
|-|-|
| `-Xplugin:kind-projector_<version>.jar` | `-Ykind-projector` |
| `-P:kind-projector:underscore-placeholders` | `-Ykind-projector:underscores` |
5 changes: 2 additions & 3 deletions docs/incompatibilities/other-changed-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ The solution depends on the situation. In the given example, you can either:

Scala 3 cannot reduce the application of a higher-kinded abstract type member to the wildcard argument.

For instance, the following example does not compile.
For instance, the following example does not compile.

```scala
Expand All @@ -271,14 +270,14 @@ We can fix this by using a type parameter:
But this simple solution does not work when `Foo` is itself used as type argument.

```scala
def g(foos: Seq[Foo[_]]): Unit`
def g(foos: Seq[Foo[_]]): Unit
```

In such case, we can use a wrapper class around `Foo`:

```diff
+class FooWrapper[A](foo: Foo[A])

-def g(foos: Seq[Foo[_]]): Unit`
-def g(foos: Seq[Foo[_]]): Unit
+def g(foos: Seq[FooWrapper[_]]): Unit
```
117 changes: 117 additions & 0 deletions docs/tutorials/kind-projector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
id: kind-projector
title: kind-projector Migration Tutorial
---

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 lambdas.

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.

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.5` and `2.12.14`.
To enable it, add the compiler flags `-Xsource:3 -P:kind-projector:underscore-placeholders` to your build.
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.

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

```scala
ThisBuild / scalacOptions ++= (if (scalaVersion.value.startsWith("3")) Seq("-Ykind-projector:underscores")
else Seq("-Xsource:3", "-P:kind-projector:underscore-placeholders"))
```

### Migrating to New Syntax

To use underscores for type-lambdas in existing kind-projector enabled code, replace `*` or `?` type lambda placeholders with `_`.

In turn, you will also have to rewrite all usages of `_` as the wildcard to use `?` symbol.
Comment on lines +23 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case are scalafix rules available for these rewrites, would be good to mention / link them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don' think we have a Scalafix rule but we have a compiler rewrite with -source:future-migration -rewrite.
However, it also patches some other patterns that could break the compatibility with Scala 2.13. So I am not sure if it is worth mentioning.
I once did the inventory of the -source:future-migration rewrite rules but it has not been updated for some time: https://github.com/scalacenter/scala-3-migration-guide/tree/main/incompat-31


For example the following usage of the wildcard:

```scala
def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = widgets.find(_.name == name)
```

Must be rewritten to:

```scala
def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = widgets.find(_.name == name)
```

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

```scala
Tuple2[*, Double] // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*] // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
```

Must be rewritten to:

```scala
Tuple2[_, Double] // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +_] // equivalent to: type R[+A] = Either[Int, A]
Function2[-_, Long, +_] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
```

### Compiling Existing Code

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

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

```scala
Tuple2[*, Double] // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*] // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*] // equivalent to: type R[-A, +B] = Function2[A, Long, B]
```

### Rewriting Incompatible Constructs

Scala 3's `-Ykind-projector` & `-Ykind-projector:underscores` implement only a subset of `kind-projector` syntax, in particular they do not implement:

* higher-kinded type lambda placeholders
* higher-kinded named type lambda parameters
* The `Lambda` keyword (`λ` is still supported)

You must rewrite ALL of the following forms:

```scala
// classic
EitherT[*[_], Int, *] // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// underscores
EitherT[_[_], Int, _] // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// named λ
λ[(F[_], A) => EitherT[F, Int, A]]
// named Lambda
Lambda[(F[_], A) => EitherT[F, Int, A]]
```

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

```scala
type MyLambda[F[_], A] = EitherT[F, Int, A]
MyLambda
```

Alternatively you may use Scala 3's [Native Type Lambdas](https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html) if you do not need to cross-compile:

```scala
[F[_], A] =>> EitherT[F, Int, A]
```

For `Lambda` you must rewrite the following form:

```scala
Lambda[(`+E`, `+A`) => Either[E, A]]
```

To the following to cross-compile:

λ[(`+E`, `+A`) => Either[E, A]]

Or alternatively to Scala 3 Type Lambdas:

```scala
[E, A] =>> Either[E, A]
```

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