From 7d15e267d1f6a7ec92c6341d45c275dd21634a37 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 8 Mar 2023 14:53:02 +0100 Subject: [PATCH 1/5] Add code tabs to Other changes page --- .../incompat-other-changes.md | 219 ++++++++++++------ 1 file changed, 153 insertions(+), 66 deletions(-) diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index 505df8c17f..8b00e9d5d5 100644 --- a/_overviews/scala3-migration/incompat-other-changes.md +++ b/_overviews/scala3-migration/incompat-other-changes.md @@ -25,14 +25,18 @@ Some other features are simplified or restricted to make the language easier, sa An inherited member, from a parent trait or class, can shadow an identifier defined in an outer scope. That pattern is called inheritance shadowing. -```scala +{% tabs scala-3-inheritance_1 %} +{% tab 'Scala 3 Only' %} +~~~ scala object B { val x = 1 class C extends A { println(x) } } -``` +~~~ +{% endtab %} +{% endtabs %} For instance, in this preceding piece of code, the `x` term in C can refer to the `x` member defined in the outer class `B` or it can refer to a `x` member of the parent class `A`. You cannot know until you go to the definition of `A`. @@ -41,9 +45,11 @@ This is known for being error prone. That's why, in Scala 3, the compiler requires disambiguation if the parent class `A` does actually have a member `x`. +{% tabs scala-3-inheritance_2 %} +{% tab 'Scala 3 Only' %} It prevents the following piece of code from compiling. -```scala +~~~ scala class A { val x = 2 } @@ -54,51 +60,59 @@ object B { println(x) } } -``` +~~~ -{% highlight text %} +~~~ text -- [E049] Reference Error: src/main/scala/inheritance-shadowing.scala:9:14 9 | println(x) | ^ | Reference to x is ambiguous, | it is both defined in object B | and inherited subsequently in class C -{% endhighlight %} +~~~ The [Scala 3 migration compilation](tooling-migration-mode.html) can automatically disambiguate the code by replacing `println(x)` with `println(this.x)`. +{% endtab %} +{% endtabs %} ## Non-private Constructor In Private Class The Scala 3 compiler requires the constructor of private classes to be private. +{% tabs scala-3-constructor_1 %} +{% tab 'Scala 3 Only' %} For instance, in the example: -```scala +~~~ scala package foo private class Bar private[foo] () {} -``` +~~~ The error message is: -``` +~~~ text -- Error: /home/piquerez/scalacenter/scala-3-migration-guide/incompat/access-modifier/src/main/scala-2.13/access-modifier.scala:4:19 4 | private class Bar private[foo] () | ^ | non-private constructor Bar in class Bar refers to private class Bar | in its type signature (): foo.Foo.Bar -``` +~~~ The [Scala 3 migration compilation](tooling-migration-mode.html) warns about this but no automatic rewrite is provided. The solution is to make the constructor private, since the class is private. +{% endtab %} +{% endtabs %} ## Abstract Override In Scala 3, overriding a concrete def with an abstract def causes subclasses to consider the def abstract, whereas in Scala 2 it was considered as concrete. -In the following piece of code, the `bar` method in `C` is considered concrete by the Scala 2.13 compiler but abstract by the Scala 3 compiler, causing the following error. +{% tabs scala-3-abstract_1 %} +{% tab 'Scala 3 Only' %} -```scala +In the following piece of code, the `bar` method in `C` is considered concrete by the Scala 2.13 compiler but abstract by the Scala 3 compiler, causing the following error. +~~~ scala trait A { def bar(x: Int): Int = x + 3 } @@ -108,44 +122,53 @@ trait B extends A { } class C extends B // Error: class C needs to be abstract, since def bar(x: Int): Int is not defined -``` +~~~ This behavior was decided in [Dotty issue #4770](https://github.com/lampepfl/dotty/issues/4770). An easy fix is simply to remove the abstract def, since in practice it had no effect in Scala 2. +{% endtab %} +{% endtabs %} ## Case Class Companion The companion object of a case class does not extend any of the `Function{0-23}` traits anymore. In particular, it does not inherit their methods: `tupled`, `curried`, `andThen`, `compose`... -For instance, this is not permitted anymore: +{% tabs companion_1 class=tabs-scala-version %} +{% tab 'Scala 2' for=companion_1 %} -```scala +For instance, this is not permitted anymore: +~~~ scala case class Foo(x: Int, b: Boolean) Foo.curried(1)(true) Foo.tupled((2, false)) -``` +~~~ +{% endtab %} +{% tab 'Scala 3' for=companion_1 %} A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. -{% highlight diff %} --Foo.curried(1)(true) -+(Foo.apply _).curried(1)(true) +~~~ scala +case class Foo(x: Int, b: Boolean) --Foo.tupled((2, false)) -+(Foo.apply _).tupled((2, false)) -{% endhighlight %} +(Foo.apply _).curried(1)(true) +(Foo.apply _).tupled((2, false)) +~~~ Or, for performance reasons, you can introduce an intermediate function value. -```scala +~~~ scala +case class Foo(x: Int, b: Boolean) + val fooCtr: (Int, Boolean) => Foo = (x, b) => Foo(x, b) fooCtr.curried(1)(true) fooCtr.tupled((2, false)) -``` +~~~ +{% endtab %} +{% endtabs %} ## Explicit Call to `unapply` In Scala, case classes have an auto-generated extractor method, called `unapply` in their companion object. @@ -156,52 +179,74 @@ The new signature is option-less (see the new [Pattern Matching]({{ site.scala3r Note that this problem does not affect user-defined extractors, whose signature stays the same across Scala versions. Given the following case class definition: - -```scala +{% tabs shared-unapply_1 %} +{% tab 'Scala 2 and 3' %} +~~~ scala case class Location(lat: Double, long: Double) -``` +~~~ +{% endtab %} +{% endtabs %} + +{% tabs unapply_2 class=tabs-scala-version %} +{% tab 'Scala 2' for=unapply_2 %} The Scala 2.13 compiler generates the following `unapply` method: -```scala +~~~ scala object Location { def unapply(location: Location): Option[(Double, Double)] = Some((location.lat, location.long)) } -``` +~~~ +{% endtab %} +{% tab 'Scala 3' for=unapply_2 %} Whereas the Scala 3 compiler generates: - -```scala +~~~ scala object Location { def unapply(location: Location): Location = location } -``` +~~~ Consequently the following code does not compile anymore. - -```scala +~~~ scala def tuple(location: Location): (Int, Int) = { Location.unapply(location).get // [E008] Not Found Error: value get is not a member of Location } -``` +~~~ +{% endtab %} +{% endtabs %} A possible solution is to use pattern binding: -{% highlight diff %} +{% tabs unaply_3 class=tabs-scala-version %} +{% tab 'Scala 2' for=unaply_3 %} + +~~~ scala def tuple(location: Location): (Int, Int) = { -- Location.unapply(location).get -+ val Location(lat, lon) = location -+ (lat, lon) +Location.unapply(location).get } -{% endhighlight %} +~~~ +{% endtab %} +{% tab 'Scala 3' for=unaply_3 %} +~~~ scala +def tuple(location: Location): (Int, Int) = { +val Location(lat, lon) = location +(lat, lon) +} +~~~ +{% endtab %} +{% endtabs %} ## Invisible Bean Property The getter and setter methods generated by the `BeanProperty` annotation are now invisible in Scala 3 because their primary use case is the interoperability with Java frameworks. +{% tabs scala-3-bean_1 %} +{% tab 'Scala 3 Only' %} + For instance, in the below example: -```scala +~~~ scala class Pojo() { @BeanProperty var fooBar: String = "" } @@ -211,19 +256,33 @@ val pojo = new Pojo() pojo.setFooBar("hello") // [E008] Not Found Error: value setFooBar is not a member of Pojo println(pojo.getFooBar()) // [E008] Not Found Error: value getFooBar is not a member of Pojo -``` +~~~ +{% endtab %} +{% endtabs %} The solution is to call the more idiomatic `pojo.fooBar` getter and setter. -{% highlight diff %} +{% tabs bean_2 class=tabs-scala-version %} +{% tab 'Scala 2' for=bean_2 %} + +~~~ scala +val pojo = new Pojo() + +pojo.setFooBar("hello") + +println(pojo.getFooBar()) +~~~ +{% endtab %} +{% tab 'Scala 3' for=bean_2 %} +~~~ scala val pojo = new Pojo() --pojo.setFooBar("hello") -+pojo.fooBar = "hello" +pojo.fooBar = "hello" --println(pojo.getFooBar()) -+println(pojo.fooBar) -{% endhighlight %} +println(pojo.fooBar) +~~~ +{% endtab %} +{% endtabs %} ## `=> T` as Type Argument @@ -232,8 +291,9 @@ A type of the form `=> T` cannot be used as an argument to a type parameter anym This decision is explained in [this comment](https://github.com/lampepfl/dotty/blob/0f1a23e008148f76fd0a1c2991b991e1dad600e8/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala#L144-L152) of the Scala 3 source code. For instance, it is not allowed to pass a function of type `Int => (=> Int) => Int` to the `uncurried` method since it would assign `=> Int` to the type parameter `T2`. - -``` +{% tabs scala-3-type_1 %} +{% tab 'Scala 3 Only' %} +~~~ scala -- [E134] Type Mismatch Error: src/main/scala/by-name-param-type-infer.scala:3:41 3 | val g: (Int, => Int) => Int = Function.uncurried(f) | ^^^^^^^^^^^^^^^^^^ @@ -244,7 +304,9 @@ For instance, it is not allowed to pass a function of type `Int => (=> Int) => I | [T1, T2, T3, R](f: T1 => T2 => T3 => R): (T1, T2, T3) => R | [T1, T2, R](f: T1 => T2 => R): (T1, T2) => R |match arguments ((Test.f : Int => (=> Int) => Int)) -``` +~~~ +{% endtab %} +{% endtabs %} The solution depends on the situation. In the given example, you can either: - define your own `uncurried` method with the appropriate signature @@ -254,34 +316,59 @@ 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. +{% tabs scala-3-wildcard_1 %} +{% tab 'Scala 3 Only' %} + For instance, the following example does not compile. -```scala +~~~ scala trait Example { type Foo[A] def f(foo: Foo[_]): Unit // [E043] Type Error: unreducible application of higher-kinded type Example.this.Foo to wildcard arguments } -``` +~~~ +{% endtab %} +{% endtabs %} We can fix this by using a type parameter: -{% highlight diff %} --def f(foo: Foo[_]): Unit -+def f[A](foo: Foo[A]): Unit -{% endhighlight %} +{% tabs wildcard_2 class=tabs-scala-version %} +{% tab 'Scala 2' for=wildcard_2 %} -But this simple solution does not work when `Foo` is itself used as a type argument. +~~~ scala +def f(foo: Foo[_]): Unit +~~~ +{% endtab %} +{% tab 'Scala 3' for=wildcard_2 %} +~~~ scala +def f[A](foo: Foo[A]): Unit +~~~ +{% endtab %} +{% endtabs %} -```scala +But this simple solution does not work when `Foo` is itself used as a type argument. +{% tabs shared-wildcard_3 %} +{% tab 'Scala 2 and 3' %} +~~~ scala def g(foos: Seq[Foo[_]]): Unit -``` +~~~ +{% endtab %} +{% endtabs %} In such case, we can use a wrapper class around `Foo`: -{% highlight diff %} -+class FooWrapper[A](foo: Foo[A]) - --def g(foos: Seq[Foo[_]]): Unit -+def g(foos: Seq[FooWrapper[_]]): Unit -{% endhighlight %} +{% tabs wildcard_4 class=tabs-scala-version %} +{% tab 'Scala 2' for=wildcard_4 %} +~~~ scala +def g(foos: Seq[Foo[_]]): Unit +~~~ +{% endtab %} +{% tab 'Scala 3' for=wildcard_4 %} +~~~ scala +class FooWrapper[A](foo: Foo[A]) + +def g(foos: Seq[FooWrapper[_]]): Unit +~~~ +{% endtab %} +{% endtabs %} \ No newline at end of file From 1c9d9bdf49d114867d5d5ce01f43570c9016f3fb Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 9 Mar 2023 10:06:44 +0100 Subject: [PATCH 2/5] Change Scala 3 to Scala 2 only for some cases --- .../incompat-other-changes.md | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index 8b00e9d5d5..7df7d25ed1 100644 --- a/_overviews/scala3-migration/incompat-other-changes.md +++ b/_overviews/scala3-migration/incompat-other-changes.md @@ -25,8 +25,8 @@ Some other features are simplified or restricted to make the language easier, sa An inherited member, from a parent trait or class, can shadow an identifier defined in an outer scope. That pattern is called inheritance shadowing. -{% tabs scala-3-inheritance_1 %} -{% tab 'Scala 3 Only' %} +{% tabs shared-inheritance_1 %} +{% tab 'Scala 2 and 3' %} ~~~ scala object B { val x = 1 @@ -45,10 +45,9 @@ This is known for being error prone. That's why, in Scala 3, the compiler requires disambiguation if the parent class `A` does actually have a member `x`. -{% tabs scala-3-inheritance_2 %} -{% tab 'Scala 3 Only' %} +{% tabs scala-2-inheritance_2 %} +{% tab 'Scala 2 Only' %} It prevents the following piece of code from compiling. - ~~~ scala class A { val x = 2 @@ -61,26 +60,27 @@ object B { } } ~~~ +{% endtab %} +{% endtabs %} -~~~ text +But if you try to compile with Scala 3 you should see an error of the same kind as: +{% highlight text %} -- [E049] Reference Error: src/main/scala/inheritance-shadowing.scala:9:14 9 | println(x) | ^ | Reference to x is ambiguous, | it is both defined in object B | and inherited subsequently in class C -~~~ +{% endhighlight %} The [Scala 3 migration compilation](tooling-migration-mode.html) can automatically disambiguate the code by replacing `println(x)` with `println(this.x)`. -{% endtab %} -{% endtabs %} ## Non-private Constructor In Private Class The Scala 3 compiler requires the constructor of private classes to be private. -{% tabs scala-3-constructor_1 %} -{% tab 'Scala 3 Only' %} +{% tabs scala-2-constructor_1 %} +{% tab 'Scala 2 Only' %} For instance, in the example: ~~~ scala @@ -88,21 +88,21 @@ package foo private class Bar private[foo] () {} ~~~ +{% endtab %} +{% endtabs %} -The error message is: -~~~ text +If you try to compile in scala 3 you should get the following error message: +{% highlight text %} -- Error: /home/piquerez/scalacenter/scala-3-migration-guide/incompat/access-modifier/src/main/scala-2.13/access-modifier.scala:4:19 4 | private class Bar private[foo] () | ^ | non-private constructor Bar in class Bar refers to private class Bar | in its type signature (): foo.Foo.Bar -~~~ +{% endhighlight %} The [Scala 3 migration compilation](tooling-migration-mode.html) warns about this but no automatic rewrite is provided. The solution is to make the constructor private, since the class is private. -{% endtab %} -{% endtabs %} ## Abstract Override @@ -149,7 +149,6 @@ Foo.tupled((2, false)) {% tab 'Scala 3' for=companion_1 %} A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. - ~~~ scala case class Foo(x: Int, b: Boolean) @@ -187,8 +186,8 @@ case class Location(lat: Double, long: Double) {% endtab %} {% endtabs %} -{% tabs unapply_2 class=tabs-scala-version %} -{% tab 'Scala 2' for=unapply_2 %} +{% tabs scala-2-unapply_2 %} +{% tab 'Scala 2 Only' %} The Scala 2.13 compiler generates the following `unapply` method: @@ -198,7 +197,10 @@ object Location { } ~~~ {% endtab %} -{% tab 'Scala 3' for=unapply_2 %} +{% endtabs %} + +{% tabs scala-3-unapply_2 %} +{% tab 'Scala 3 Only' %} Whereas the Scala 3 compiler generates: ~~~ scala @@ -223,15 +225,15 @@ A possible solution is to use pattern binding: ~~~ scala def tuple(location: Location): (Int, Int) = { -Location.unapply(location).get + Location.unapply(location).get } ~~~ {% endtab %} {% tab 'Scala 3' for=unaply_3 %} ~~~ scala def tuple(location: Location): (Int, Int) = { -val Location(lat, lon) = location -(lat, lon) + val Location(lat, lon) = location + (lat, lon) } ~~~ {% endtab %} @@ -291,9 +293,8 @@ A type of the form `=> T` cannot be used as an argument to a type parameter anym This decision is explained in [this comment](https://github.com/lampepfl/dotty/blob/0f1a23e008148f76fd0a1c2991b991e1dad600e8/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala#L144-L152) of the Scala 3 source code. For instance, it is not allowed to pass a function of type `Int => (=> Int) => Int` to the `uncurried` method since it would assign `=> Int` to the type parameter `T2`. -{% tabs scala-3-type_1 %} -{% tab 'Scala 3 Only' %} -~~~ scala + +{% highlight text %} -- [E134] Type Mismatch Error: src/main/scala/by-name-param-type-infer.scala:3:41 3 | val g: (Int, => Int) => Int = Function.uncurried(f) | ^^^^^^^^^^^^^^^^^^ @@ -304,9 +305,7 @@ For instance, it is not allowed to pass a function of type `Int => (=> Int) => I | [T1, T2, T3, R](f: T1 => T2 => T3 => R): (T1, T2, T3) => R | [T1, T2, R](f: T1 => T2 => R): (T1, T2) => R |match arguments ((Test.f : Int => (=> Int) => Int)) -~~~ -{% endtab %} -{% endtabs %} +{% endhighlight %} The solution depends on the situation. In the given example, you can either: - define your own `uncurried` method with the appropriate signature From 14338888e0ddf2a44efb18242108c70d7a47355b Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 10 Mar 2023 09:55:41 +0100 Subject: [PATCH 3/5] Corrections of the tabs. Corrections of the tabs. - Change scala 3 only by scala 2 only - Re-add highlight parts Delete Darwin dependencies --- .../incompat-other-changes.md | 145 +++++++----------- 1 file changed, 56 insertions(+), 89 deletions(-) diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index 7df7d25ed1..5b82d012c0 100644 --- a/_overviews/scala3-migration/incompat-other-changes.md +++ b/_overviews/scala3-migration/incompat-other-changes.md @@ -108,8 +108,8 @@ The solution is to make the constructor private, since the class is private. In Scala 3, overriding a concrete def with an abstract def causes subclasses to consider the def abstract, whereas in Scala 2 it was considered as concrete. -{% tabs scala-3-abstract_1 %} -{% tab 'Scala 3 Only' %} +{% tabs scala-2-abstract_1 %} +{% tab 'Scala 2 Only' %} In the following piece of code, the `bar` method in `C` is considered concrete by the Scala 2.13 compiler but abstract by the Scala 3 compiler, causing the following error. ~~~ scala @@ -121,7 +121,7 @@ trait B extends A { def bar(x: Int): Int } -class C extends B // Error: class C needs to be abstract, since def bar(x: Int): Int is not defined +class C extends B // In Scala 3, Error: class C needs to be abstract, since def bar(x: Int): Int is not defined ~~~ This behavior was decided in [Dotty issue #4770](https://github.com/lampepfl/dotty/issues/4770). @@ -135,8 +135,8 @@ An easy fix is simply to remove the abstract def, since in practice it had no ef The companion object of a case class does not extend any of the `Function{0-23}` traits anymore. In particular, it does not inherit their methods: `tupled`, `curried`, `andThen`, `compose`... -{% tabs companion_1 class=tabs-scala-version %} -{% tab 'Scala 2' for=companion_1 %} +{% tabs scala-2-companion_1 %} +{% tab 'Scala 2 Only' %} For instance, this is not permitted anymore: ~~~ scala @@ -146,21 +146,23 @@ Foo.curried(1)(true) Foo.tupled((2, false)) ~~~ {% endtab %} -{% tab 'Scala 3' for=companion_1 %} +{% endtabs %} A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. -~~~ scala -case class Foo(x: Int, b: Boolean) +{% highlight diff %} -(Foo.apply _).curried(1)(true) -(Foo.apply _).tupled((2, false)) -~~~ +-Foo.curried(1)(true) ++(Foo.apply _).curried(1)(true) -Or, for performance reasons, you can introduce an intermediate function value. +-Foo.tupled((2, false)) ++(Foo.apply _).tupled((2, false)) +{% endhighlight %} -~~~ scala -case class Foo(x: Int, b: Boolean) +{% tabs scala-3-companion_2 %} +{% tab 'Scala 3 Only' %} +Or, for performance reasons, you can introduce an intermediate function value. +~~~ scala val fooCtr: (Int, Boolean) => Foo = (x, b) => Foo(x, b) fooCtr.curried(1)(true) @@ -208,45 +210,39 @@ object Location { def unapply(location: Location): Location = location } ~~~ - -Consequently the following code does not compile anymore. -~~~ scala -def tuple(location: Location): (Int, Int) = { - Location.unapply(location).get // [E008] Not Found Error: value get is not a member of Location -} -~~~ {% endtab %} {% endtabs %} -A possible solution is to use pattern binding: - -{% tabs unaply_3 class=tabs-scala-version %} -{% tab 'Scala 2' for=unaply_3 %} +{% tabs scala-2-unapply_3 %} +{% tab 'Scala 2 Only' %} +Consequently the following code does not compile anymore in Scala 3. ~~~ scala def tuple(location: Location): (Int, Int) = { - Location.unapply(location).get + Location.unapply(location).get // [E008] In Scala 3, Not Found Error: value get is not a member of Location } ~~~ {% endtab %} -{% tab 'Scala 3' for=unaply_3 %} -~~~ scala +{% endtabs %} + +A possible solution, in Scala 3, is to use pattern binding: + +{% highlight diff %} def tuple(location: Location): (Int, Int) = { - val Location(lat, lon) = location - (lat, lon) +- Location.unapply(location).get ++ val Location(lat, lon) = location ++ (lat, lon) } -~~~ -{% endtab %} -{% endtabs %} +{% endhighlight %} ## Invisible Bean Property The getter and setter methods generated by the `BeanProperty` annotation are now invisible in Scala 3 because their primary use case is the interoperability with Java frameworks. -{% tabs scala-3-bean_1 %} -{% tab 'Scala 3 Only' %} +{% tabs scala-2-bean_1 %} +{% tab 'Scala 2 Only' %} -For instance, in the below example: +For instance, the below Scala 2 code would fail to compile in Scala 3: ~~~ scala class Pojo() { @@ -255,36 +251,24 @@ class Pojo() { val pojo = new Pojo() -pojo.setFooBar("hello") // [E008] Not Found Error: value setFooBar is not a member of Pojo +pojo.setFooBar("hello") // [E008] In Scala 3, Not Found Error: value setFooBar is not a member of Pojo -println(pojo.getFooBar()) // [E008] Not Found Error: value getFooBar is not a member of Pojo +println(pojo.getFooBar()) // [E008] In Scala 3, Not Found Error: value getFooBar is not a member of Pojo ~~~ {% endtab %} {% endtabs %} -The solution is to call the more idiomatic `pojo.fooBar` getter and setter. - -{% tabs bean_2 class=tabs-scala-version %} -{% tab 'Scala 2' for=bean_2 %} +In Scala 3, the solution is to call the more idiomatic `pojo.fooBar` getter and setter. -~~~ scala -val pojo = new Pojo() - -pojo.setFooBar("hello") - -println(pojo.getFooBar()) -~~~ -{% endtab %} -{% tab 'Scala 3' for=bean_2 %} -~~~ scala +{% highlight diff %} val pojo = new Pojo() -pojo.fooBar = "hello" +-pojo.setFooBar("hello") ++pojo.fooBar = "hello" -println(pojo.fooBar) -~~~ -{% endtab %} -{% endtabs %} +-println(pojo.getFooBar()) ++println(pojo.fooBar) +{% endhighlight %} ## `=> T` as Type Argument @@ -315,16 +299,16 @@ 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. -{% tabs scala-3-wildcard_1 %} -{% tab 'Scala 3 Only' %} +{% tabs scala-2-wildcard_1 %} +{% tab 'Scala 2 Only' %} -For instance, the following example does not compile. +For instance, the below Scala 2 code would fail to compile in Scala 3: ~~~ scala trait Example { type Foo[A] - def f(foo: Foo[_]): Unit // [E043] Type Error: unreducible application of higher-kinded type Example.this.Foo to wildcard arguments + def f(foo: Foo[_]): Unit // [E043] In Scala 3, Type Error: unreducible application of higher-kinded type Example.this.Foo to wildcard arguments } ~~~ {% endtab %} @@ -332,23 +316,14 @@ trait Example { We can fix this by using a type parameter: -{% tabs wildcard_2 class=tabs-scala-version %} -{% tab 'Scala 2' for=wildcard_2 %} - -~~~ scala -def f(foo: Foo[_]): Unit -~~~ -{% endtab %} -{% tab 'Scala 3' for=wildcard_2 %} -~~~ scala -def f[A](foo: Foo[A]): Unit -~~~ -{% endtab %} -{% endtabs %} +{% highlight diff %} +-def f(foo: Foo[_]): Unit ++def f[A](foo: Foo[A]): Unit +{% endhighlight %} But this simple solution does not work when `Foo` is itself used as a type argument. -{% tabs shared-wildcard_3 %} -{% tab 'Scala 2 and 3' %} +{% tabs scala-2-wildcard_2 %} +{% tab 'Scala 2 Only' %} ~~~ scala def g(foos: Seq[Foo[_]]): Unit ~~~ @@ -357,17 +332,9 @@ def g(foos: Seq[Foo[_]]): Unit In such case, we can use a wrapper class around `Foo`: -{% tabs wildcard_4 class=tabs-scala-version %} -{% tab 'Scala 2' for=wildcard_4 %} -~~~ scala -def g(foos: Seq[Foo[_]]): Unit -~~~ -{% endtab %} -{% tab 'Scala 3' for=wildcard_4 %} -~~~ scala -class FooWrapper[A](foo: Foo[A]) +{% highlight diff %} ++class FooWrapper[A](foo: Foo[A]) -def g(foos: Seq[FooWrapper[_]]): Unit -~~~ -{% endtab %} -{% endtabs %} \ No newline at end of file +-def g(foos: Seq[Foo[_]]): Unit ++def g(foos: Seq[FooWrapper[_]]): Unit +{% endhighlight %} \ No newline at end of file From fdd1cc5bceb39967266484285f27ff041cf1fa3e Mon Sep 17 00:00:00 2001 From: Lucas Date: Sat, 11 Mar 2023 19:39:06 +0100 Subject: [PATCH 4/5] Put only the code snippet in the tab --- .../incompat-other-changes.md | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index 5b82d012c0..aed0ea9eea 100644 --- a/_overviews/scala3-migration/incompat-other-changes.md +++ b/_overviews/scala3-migration/incompat-other-changes.md @@ -45,9 +45,9 @@ This is known for being error prone. That's why, in Scala 3, the compiler requires disambiguation if the parent class `A` does actually have a member `x`. +It prevents the following piece of code from compiling. {% tabs scala-2-inheritance_2 %} {% tab 'Scala 2 Only' %} -It prevents the following piece of code from compiling. ~~~ scala class A { val x = 2 @@ -79,10 +79,9 @@ The [Scala 3 migration compilation](tooling-migration-mode.html) can automatical The Scala 3 compiler requires the constructor of private classes to be private. +For instance, in the example: {% tabs scala-2-constructor_1 %} {% tab 'Scala 2 Only' %} -For instance, in the example: - ~~~ scala package foo @@ -108,10 +107,9 @@ The solution is to make the constructor private, since the class is private. In Scala 3, overriding a concrete def with an abstract def causes subclasses to consider the def abstract, whereas in Scala 2 it was considered as concrete. +In the following piece of code, the `bar` method in `C` is considered concrete by the Scala 2.13 compiler but abstract by the Scala 3 compiler, causing the following error. {% tabs scala-2-abstract_1 %} {% tab 'Scala 2 Only' %} - -In the following piece of code, the `bar` method in `C` is considered concrete by the Scala 2.13 compiler but abstract by the Scala 3 compiler, causing the following error. ~~~ scala trait A { def bar(x: Int): Int = x + 3 @@ -123,22 +121,21 @@ trait B extends A { class C extends B // In Scala 3, Error: class C needs to be abstract, since def bar(x: Int): Int is not defined ~~~ +{% endtab %} +{% endtabs %} This behavior was decided in [Dotty issue #4770](https://github.com/lampepfl/dotty/issues/4770). An easy fix is simply to remove the abstract def, since in practice it had no effect in Scala 2. -{% endtab %} -{% endtabs %} ## Case Class Companion The companion object of a case class does not extend any of the `Function{0-23}` traits anymore. In particular, it does not inherit their methods: `tupled`, `curried`, `andThen`, `compose`... +For instance, this is not permitted anymore: {% tabs scala-2-companion_1 %} {% tab 'Scala 2 Only' %} - -For instance, this is not permitted anymore: ~~~ scala case class Foo(x: Int, b: Boolean) @@ -158,10 +155,9 @@ A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. +(Foo.apply _).tupled((2, false)) {% endhighlight %} +Or, for performance reasons, you can introduce an intermediate function value. {% tabs scala-3-companion_2 %} {% tab 'Scala 3 Only' %} - -Or, for performance reasons, you can introduce an intermediate function value. ~~~ scala val fooCtr: (Int, Boolean) => Foo = (x, b) => Foo(x, b) @@ -188,11 +184,9 @@ case class Location(lat: Double, long: Double) {% endtab %} {% endtabs %} +The Scala 2.13 compiler generates the following `unapply` method: {% tabs scala-2-unapply_2 %} {% tab 'Scala 2 Only' %} - -The Scala 2.13 compiler generates the following `unapply` method: - ~~~ scala object Location { def unapply(location: Location): Option[(Double, Double)] = Some((location.lat, location.long)) @@ -201,10 +195,9 @@ object Location { {% endtab %} {% endtabs %} +Whereas the Scala 3 compiler generates: {% tabs scala-3-unapply_2 %} {% tab 'Scala 3 Only' %} - -Whereas the Scala 3 compiler generates: ~~~ scala object Location { def unapply(location: Location): Location = location @@ -213,10 +206,9 @@ object Location { {% endtab %} {% endtabs %} +Consequently the following code does not compile anymore in Scala 3. {% tabs scala-2-unapply_3 %} {% tab 'Scala 2 Only' %} - -Consequently the following code does not compile anymore in Scala 3. ~~~ scala def tuple(location: Location): (Int, Int) = { Location.unapply(location).get // [E008] In Scala 3, Not Found Error: value get is not a member of Location @@ -239,11 +231,9 @@ def tuple(location: Location): (Int, Int) = { The getter and setter methods generated by the `BeanProperty` annotation are now invisible in Scala 3 because their primary use case is the interoperability with Java frameworks. +For instance, the below Scala 2 code would fail to compile in Scala 3: {% tabs scala-2-bean_1 %} {% tab 'Scala 2 Only' %} - -For instance, the below Scala 2 code would fail to compile in Scala 3: - ~~~ scala class Pojo() { @BeanProperty var fooBar: String = "" @@ -299,11 +289,9 @@ 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 below Scala 2 code would fail to compile in Scala 3: {% tabs scala-2-wildcard_1 %} {% tab 'Scala 2 Only' %} - -For instance, the below Scala 2 code would fail to compile in Scala 3: - ~~~ scala trait Example { type Foo[A] From 845983dd5efa2e31916d9efd0c00cd3ed61f483b Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Mon, 13 Mar 2023 08:59:38 +0100 Subject: [PATCH 5/5] Update _overviews/scala3-migration/incompat-other-changes.md --- _overviews/scala3-migration/incompat-other-changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index aed0ea9eea..90c04abab9 100644 --- a/_overviews/scala3-migration/incompat-other-changes.md +++ b/_overviews/scala3-migration/incompat-other-changes.md @@ -157,7 +157,7 @@ A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. Or, for performance reasons, you can introduce an intermediate function value. {% tabs scala-3-companion_2 %} -{% tab 'Scala 3 Only' %} +{% tab 'Scala 2 and 3' %} ~~~ scala val fooCtr: (Int, Boolean) => Foo = (x, b) => Foo(x, b)