diff --git a/docs/compiler-options/compiler-options-table.md b/docs/compiler-options/compiler-options-table.md index cdc5067e..c89082a2 100644 --- a/docs/compiler-options/compiler-options-table.md +++ b/docs/compiler-options/compiler-options-table.md @@ -272,3 +272,4 @@ You can enable and configure them with some new native options. | 2.13.x | 3.0.x | |-|-| | `-Xplugin:kind-projector_.jar` | `-Ykind-projector` | +| `-P:kind-projector:underscore-placeholders` | `-Ykind-projector:underscores` | diff --git a/docs/incompatibilities/other-changed-features.md b/docs/incompatibilities/other-changed-features.md index d9fccaa6..4c88dc7a 100644 --- a/docs/incompatibilities/other-changed-features.md +++ b/docs/incompatibilities/other-changed-features.md @@ -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 @@ -271,7 +270,7 @@ 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`: @@ -279,6 +278,6 @@ 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 ``` diff --git a/docs/tutorials/kind-projector.md b/docs/tutorials/kind-projector.md new file mode 100644 index 00000000..a514edd9 --- /dev/null +++ b/docs/tutorials/kind-projector.md @@ -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. + +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.