diff --git a/_overviews/scala3-migration/incompat-type-inference.md b/_overviews/scala3-migration/incompat-type-inference.md index a5f1552c06..bb6fc3052a 100644 --- a/_overviews/scala3-migration/incompat-type-inference.md +++ b/_overviews/scala3-migration/incompat-type-inference.md @@ -14,26 +14,15 @@ The new algorithm is better than the old one, but sometime it can fail where Sca > It is always good practice to write the result types of all public values and methods explicitly. > It prevents the public API of your library from changing with the Scala version, because of different inferred types. -> +> > 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. ## Return Type of an Override Method 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. -```scala -class Parent { - def foo: Foo = new Foo -} - -class Child extends Parent { - override def foo = new RichFoo(super.foo) -} -``` - -In this example, `Child#foo` returns a `RichFoo` in Scala 2.13 but a `Foo` in Scala 3. -It can lead to compiler errors as demonstrated below. - +{% tabs define_parent_child %} +{% tab 'Scala 2 and 3' %} ```scala class Foo @@ -48,13 +37,24 @@ class Parent { class Child extends Parent { override def foo = new RichFoo(super.foo) } +``` +{% endtab %} +{% endtabs %} + +In this example, `Child#foo` returns a `RichFoo` in Scala 2.13 but a `Foo` in Scala 3. +It can lead to compiler errors as demonstrated below. +{% tabs extend_parent_child %} +{% tab 'Scala 3 Only' %} +```scala (new Child).foo.show // Scala 3 error: value show is not a member of Foo ``` +{% endtab %} +{% endtabs %} In some rare cases involving implicit conversions and runtime casting it could even cause a runtime failure. -The solution is to make the return type of the override method explicit: +The solution is to make the return type of the override method explicit so that it matches what is inferred in 2.13: {% highlight diff %} class Child extends Parent { @@ -68,19 +68,32 @@ class Child extends Parent { Scala 2 reflective calls are dropped and replaced by the broader [Programmatic Structural Types]({{ site.scala3ref }}/changed-features/structural-types.html). Scala 3 can imitate Scala 2 reflective calls by making `scala.reflect.Selectable.reflectiveSelectable` available wherever `scala.language.reflectiveCalls` is imported. -However the Scala 3 compiler does not infer structural types by default, and thus fails at compiling: +{% tabs define_structural %} +{% tab 'Scala 2 and 3' %} ```scala import scala.language.reflectiveCalls val foo = new { def bar: Unit = ??? } +``` +{% endtab %} +{% endtabs %} +However the Scala 3 compiler does not infer structural types by default. +It infers the type `Object` for `foo` instead of `{ def bar: Unit }`. +Therefore, the following structural selection fails to compile: + +{% tabs use_structural %} +{% tab 'Scala 3 Only' %} +```scala foo.bar // Error: value bar is not a member of Object ``` +{% endtab %} +{% endtabs %} -The straightforward solution is to write down the structural type. +The straightforward solution is to explicitly write down the structural type. {% highlight diff %} import scala.language.reflectiveCalls diff --git a/_overviews/scala3-migration/plugin-kind-projector.md b/_overviews/scala3-migration/plugin-kind-projector.md index 39639d1281..ab996ec586 100644 --- a/_overviews/scala3-migration/plugin-kind-projector.md +++ b/_overviews/scala3-migration/plugin-kind-projector.md @@ -9,11 +9,11 @@ next-page: external-resources 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. -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. +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.6` 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. +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: @@ -34,31 +34,49 @@ In turn, you will also have to rewrite all usages of `_` as the wildcard to use For example the following usage of the wildcard: +{% tabs wildcard_scala2 %} +{% tab 'Scala 2 Only' %} ```scala -def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) +def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = + widgets.find(_.name == name) ``` +{% endtab %} +{% endtabs %} Must be rewritten to: +{% tabs wildcard_scala3 %} +{% tab 'Scala 3 Only' %} ```scala -def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) +def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = + widgets.find(_.name == name) ``` +{% endtab %} +{% endtabs %} And the following usages of kind-projector's `*` placeholder: +{% tabs kind_projector_scala2 %} +{% tab 'Scala 2 Only' %} ```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] ``` +{% endtab %} +{% endtabs %} Must be rewritten to: +{% tabs kind_projector_scala3 %} +{% tab 'Scala 3 Only' %} ```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] ``` +{% endtab %} +{% endtabs %} ## Compiling Existing Code @@ -66,11 +84,15 @@ Even without migrating to underscore type lambdas, you will likely be able to co Use the flag `-Ykind-projector` to enable support for `*`-based type lambdas (without enabling underscore type lambdas), the following forms will now compile: +{% tabs kind_projector_cross %} +{% tab 'Scala 2 and 3' %} ```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] ``` +{% endtab %} +{% endtabs %} ## Rewriting Incompatible Constructs @@ -82,6 +104,8 @@ Scala 3's `-Ykind-projector` & `-Ykind-projector:underscores` implement only a s You must rewrite ALL of the following forms: +{% tabs kind_projector_illegal_scala2 %} +{% tab 'Scala 2 Only' %} ```scala // classic 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] // named Lambda Lambda[(F[_], A) => EitherT[F, Int, A]] ``` +{% endtab %} +{% endtabs %} Into the following long-form to cross-compile with Scala 3: +{% tabs kind_projector_illegal_cross %} +{% tab 'Scala 2 and 3' %} ```scala type MyLambda[F[_], A] = EitherT[F, Int, A] MyLambda ``` +{% endtab %} +{% endtabs %} 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: +{% tabs kind_projector_illegal_scala3 %} +{% tab 'Scala 3 Only' %} ```scala [F[_], A] =>> EitherT[F, Int, A] ``` +{% endtab %} +{% endtabs %} For `Lambda` you must rewrite the following form: +{% tabs kind_projector_illegal_lambda_scala2 %} +{% tab 'Scala 2 Only' %} ```scala Lambda[(`+E`, `+A`) => Either[E, A]] ``` +{% endtab %} +{% endtabs %} To the following to cross-compile: +{% tabs kind_projector_illegal_lambda_cross %} +{% tab 'Scala 2 and 3' %} ```scala λ[(`+E`, `+A`) => Either[E, A]] ``` +{% endtab %} +{% endtabs %} Or alternatively to Scala 3 type lambdas: +{% tabs kind_projector_illegal_lambda_scala3 %} +{% tab 'Scala 3 Only' %} ```scala [E, A] =>> Either[E, A] ``` +{% endtab %} +{% endtabs %} Note: Scala 3 type lambdas no longer need `-` or `+` variance markers on parameters, these are now inferred. diff --git a/_overviews/scala3-migration/tutorial-macro-cross-building.md b/_overviews/scala3-migration/tutorial-macro-cross-building.md index b456b521aa..8d45ed3f90 100644 --- a/_overviews/scala3-migration/tutorial-macro-cross-building.md +++ b/_overviews/scala3-migration/tutorial-macro-cross-building.md @@ -101,11 +101,11 @@ If you try to compile with Scala 3 you should see some errors of the same kind a {% highlight text %} sbt:example> ++3.3.1 sbt:example> example / compile -[error] -- Error: /example/src/main/scala/location/Location.scala:15:35 +[error] -- Error: /example/src/main/scala/location/Location.scala:15:35 [error] 15 | val location = typeOf[Location] [error] | ^ [error] | No TypeTag available for location.Location -[error] -- Error: /example/src/main/scala/location/Location.scala:18:4 +[error] -- Error: /example/src/main/scala/location/Location.scala:18:4 [error] 18 | q"new $location($path, $line)" [error] | ^ [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. package location object Macros: - def location: Location = ??? + inline def location: Location = ??? ``` {% endtab %} {% endtabs %} @@ -191,7 +191,7 @@ object Macros: private def locationImpl(using quotes: Quotes): Expr[Location] = import quotes.reflect.Position val pos = Position.ofMacroExpansion - val file = Expr(pos.sourceFile.jpath.toString) + val file = Expr(pos.sourceFile.path.toString) val line = Expr(pos.startLine + 1) '{new Location($file, $line)} ``` diff --git a/_overviews/scala3-migration/tutorial-macro-mixing.md b/_overviews/scala3-migration/tutorial-macro-mixing.md index 34389b9d1b..4a013b4284 100644 --- a/_overviews/scala3-migration/tutorial-macro-mixing.md +++ b/_overviews/scala3-migration/tutorial-macro-mixing.md @@ -7,7 +7,7 @@ previous-page: tutorial-macro-mixing next-page: tooling-syntax-rewriting --- -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. +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. There are two main benefits of this: