diff --git a/_overviews/scala3-migration/incompat-other-changes.md b/_overviews/scala3-migration/incompat-other-changes.md index 505df8c17f..90c04abab9 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 shared-inheritance_1 %} +{% tab 'Scala 2 and 3' %} +~~~ 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`. @@ -42,8 +46,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. - -```scala +{% tabs scala-2-inheritance_2 %} +{% tab 'Scala 2 Only' %} +~~~ scala class A { val x = 2 } @@ -54,8 +59,11 @@ object B { println(x) } } -``` +~~~ +{% endtab %} +{% endtabs %} +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) @@ -72,21 +80,24 @@ 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: - -```scala +{% tabs scala-2-constructor_1 %} +{% tab 'Scala 2 Only' %} +~~~ scala package foo private class Bar private[foo] () {} -``` +~~~ +{% endtab %} +{% endtabs %} -The error message is: -``` +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. @@ -97,8 +108,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. - -```scala +{% tabs scala-2-abstract_1 %} +{% tab 'Scala 2 Only' %} +~~~ scala trait A { def bar(x: Int): Int = x + 3 } @@ -107,8 +119,10 @@ 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 +~~~ +{% endtab %} +{% endtabs %} This behavior was decided in [Dotty issue #4770](https://github.com/lampepfl/dotty/issues/4770). @@ -120,17 +134,20 @@ The companion object of a case class does not extend any of the `Function{0-23}` In particular, it does not inherit their methods: `tupled`, `curried`, `andThen`, `compose`... For instance, this is not permitted anymore: - -```scala +{% tabs scala-2-companion_1 %} +{% tab 'Scala 2 Only' %} +~~~ scala case class Foo(x: Int, b: Boolean) Foo.curried(1)(true) Foo.tupled((2, false)) -``` +~~~ +{% endtab %} +{% endtabs %} A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. - {% highlight diff %} + -Foo.curried(1)(true) +(Foo.apply _).curried(1)(true) @@ -139,13 +156,16 @@ A cross-compiling solution is to explicitly eta-expand the method `Foo.apply`. {% endhighlight %} Or, for performance reasons, you can introduce an intermediate function value. - -```scala +{% tabs scala-3-companion_2 %} +{% tab 'Scala 2 and 3' %} +~~~ scala 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,36 +176,48 @@ 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 %} The Scala 2.13 compiler generates the following `unapply` method: - -```scala +{% tabs scala-2-unapply_2 %} +{% tab 'Scala 2 Only' %} +~~~ scala object Location { def unapply(location: Location): Option[(Double, Double)] = Some((location.lat, location.long)) } -``` +~~~ +{% endtab %} +{% endtabs %} Whereas the Scala 3 compiler generates: - -```scala +{% tabs scala-3-unapply_2 %} +{% tab 'Scala 3 Only' %} +~~~ scala object Location { def unapply(location: Location): Location = location } -``` - -Consequently the following code does not compile anymore. - -```scala +~~~ +{% endtab %} +{% endtabs %} + +Consequently the following code does not compile anymore in Scala 3. +{% tabs scala-2-unapply_3 %} +{% tab 'Scala 2 Only' %} +~~~ scala def tuple(location: Location): (Int, Int) = { - Location.unapply(location).get // [E008] Not Found Error: value get is not a member of Location + Location.unapply(location).get // [E008] In Scala 3, Not Found Error: value get is not a member of Location } -``` +~~~ +{% endtab %} +{% endtabs %} -A possible solution is to use pattern binding: +A possible solution, in Scala 3, is to use pattern binding: {% highlight diff %} def tuple(location: Location): (Int, Int) = { @@ -199,21 +231,24 @@ 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, in the below example: - -```scala +For instance, the below Scala 2 code would fail to compile in Scala 3: +{% tabs scala-2-bean_1 %} +{% tab 'Scala 2 Only' %} +~~~ scala class Pojo() { @BeanProperty var fooBar: String = "" } 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. +In Scala 3, the solution is to call the more idiomatic `pojo.fooBar` getter and setter. {% highlight diff %} val pojo = new Pojo() @@ -233,7 +268,7 @@ This decision is explained in [this comment](https://github.com/lampepfl/dotty/b 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`. -``` +{% 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) | ^^^^^^^^^^^^^^^^^^ @@ -244,7 +279,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)) -``` +{% endhighlight %} The solution depends on the situation. In the given example, you can either: - define your own `uncurried` method with the appropriate signature @@ -254,15 +289,18 @@ 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. - -```scala +For instance, the below Scala 2 code would fail to compile in Scala 3: +{% tabs scala-2-wildcard_1 %} +{% tab 'Scala 2 Only' %} +~~~ 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 %} +{% endtabs %} We can fix this by using a type parameter: @@ -272,10 +310,13 @@ We can fix this by using a type parameter: {% endhighlight %} But this simple solution does not work when `Foo` is itself used as a type argument. - -```scala +{% tabs scala-2-wildcard_2 %} +{% tab 'Scala 2 Only' %} +~~~ scala def g(foos: Seq[Foo[_]]): Unit -``` +~~~ +{% endtab %} +{% endtabs %} In such case, we can use a wrapper class around `Foo`: @@ -284,4 +325,4 @@ In such case, we can use a wrapper class around `Foo`: -def g(foos: Seq[Foo[_]]): Unit +def g(foos: Seq[FooWrapper[_]]): Unit -{% endhighlight %} +{% endhighlight %} \ No newline at end of file