id | title |
---|---|
incompat-31 |
Dotty future-migration Mode |
Some deprecations have been postponed to 3.1 to facilitate the migration from 2.13 to 3.0 and then from 3.0 to 3.1. However some of the migration rules are already available and you are likely to be able to apply them in your codebase. In this way you get accustomed to the new syntax and prepared for 3.1.
Beware though that some of the future-migration
rewrites break the source compatibility with Scala 2.13.
The deprecation of the _
syntax for wildcard type arguments has be postponed to Scala 3.1 and it will be replaced by the ?
syntax. You can find the motivation for this in the Dotty reference.
However there already is a migration rule that you can apply using some slightly different options than before: dotc -source:future-migration -rewrite
. Note the 3.1
version mentioned here.
For example:
def compare(x: Container[_], y: Container[_]): Int = {
x.weight - y.weight
}
Is rewritten into
def compare(x: Container[?], y: Container[?]): Int = {
x.weight - y.weight
}
Will be available in Dotty 0.26.0 or Dotty 0.27.0-RC1
Starting from Scala 3.1, it will be required to enclose the implicit parameter of a lambda in parentheses, making the following Scala 3 code illegal.
val f = { implicit x: Context => ??? }
Compiling with dotc -source:future-migration -rewrite
rewrites it into:
val f = { (implicit x: Context) => ??? }
Starting from Scala 3.1, alphanumeric methods should be annotated with @infix
to be used as infix operators (see Dotty documentation).
The -deprecation
mode of the compiler will warn you foreach infix call of un-annotated methods.
Here, the call of the difference
method is deprecated:
trait MultiSet {
def difference(other: MultiSet): MultiSet
}
def test(s1: MultiSet, s2: MultiSet): MultiSet =
s1 difference s2
The compiler can backquote the method call under the -source:3.1 -deprecation -rewrite
options.
trait MultiSet {
def difference(other: MultiSet): MultiSet
}
def test(s1: MultiSet, s2: MultiSet): MultiSet =
s1 `difference` s2
Will be available in Dotty 0.26.0 or Dotty 0.27.0-RC1
From Scala 3.1 on, pattern binding will require to be type consistent in order to prevent undesired runtime errors. See Dotty documentation for more information on this.
This piece of code compiles in Scala 2.13 and Scala 3.0 but not in Scala 3.1:
val list: List[Int] = List(1)
val head :: _ = list
You can use the @unchecked
annotation to tell the compiler to ignore that the binding can fail.
Compiling with dotc -source:future-migration -rewrite
can write it automatically.
val list: List[Int] = List(1)
val head :: _: @unchecked = list
Similarly, in a for
expression, this piece of code compiles in Scala 2.13 and Scala 3.0 but not in Scala 3.1:
val listOpt: List[Option[Int]] = List(Some(1), None)
for (Some(value) <- listOpt) println(value)
In Scala 2 and Scala 3.0, the elements of listOpt
are filtered to retain only the value of type Some
.
In Scala 3.1, this syntax does not induce filtering, but the binding is type checked to prevent runtime errors.
You can still have the same behavior than Scala 2 by adding the case
keyword.
Compiling with dotc -source:future-migration -rewrite
can add it for you automatically.
val listOpt: List[Option[Int]] = List(Some(1), None)
for (case Some(value) <- listOpt) println(value)
This rule breaks the source compatibility with Scala 2.13.
The method value syntax m _
will no longer be supported in Scala 3.1, since we now have automatic eta-expansion.
In general you can simply drop the _
symbol.
Compiling with dotc -source:future-migration -rewrite
rewrites
def foo(x: Int)(y: Int): Int = x + y
val f = foo _
Into
def foo(x: Int)(y: Int): Int = x + y
val f = foo
In the special case of a nullary method, the rewrite rule transforms
def bar(): Int = 3
val g = bar _
Into
def bar(): Int = 3
val g = (() => bar())
This rule breaks the source compatibility with Scala 2.13.
From Scala 3.1 on, context bounds will map to context parameters.
Thus a using
clause is needed to pass explicit arguments to them.
Compiling with dotc -source:future-migration -rewrite
rewrites
def show[T: Show](value: T): String = ???
val intShow = new Show[Int] {}
show(5)(intShow)
Into
def show[T: Show](value: T): String = ???
val intShow = new Show[Int] {}
show(5)(using intShow)