Scala 3.4から新たに警告やエラーになる予定の記法を雑にまとめ

qiita.com

これは Scala Advent Calendar 2023 の16日目です。

(あとから見たらこの日付が抜けていたので、あとから登録して埋めました)

これ書いている2023年12月現在、(RCなどの安定版以外も含めると) 3.4.0-RC1が最新で、3.4.0のfinalは出ていませんが、3.4.0から色々と変更点があるみたいなので、3.4.0-RC1時点の情報をざっくりまとめます。

あくまで自分が遭遇した、あるいはざっくりリリースノート見て書き出しただけなので、他にも大量にありそうですが、ひとまずこの程度にしておきます。

https://github.com/lampepfl/dotty/releases/tag/3.4.0-RC1

各種書き換えは、scalafmtの設定だけで可能な場合もあるし、scalafixでやった方がいいパターンもあり得るかもしれないし、あるいは、(現状でも?少し待てば?)Scala 3自体の -rewrite オプションでやってくれるパターンもありそうですが、そこの詳細については調べきれてないので、今回はまとめません。

また、細かいコンパイルオプションの指定の有無によって細かい挙動は変わるはずですが、あくまで何も指定してないのに警告やエラーが増えるパターンについて書いています。

また、大抵はScala 2で -Xsource:3 付与すれば、Scala 2と3でのcross buildは、新しい記法に揃える方法で、困らないようになってるはず・・・(一部を除く)

ワイルドカード_ が非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> val xs: List[_] = Nil
1 warning found
-- Warning: --------------------------------------------------------------------
1 |val xs: List[_] = Nil
  |             ^
  |        `_` is deprecated for wildcard arguments of types: use `?` instead
val xs: List[?] = List()
                                                                                                                                             
scala> val xs: List[?] = Nil
val xs: List[?] = List()

private[this]protected[this] が非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> class A { protected[this] def x = 2 }
1 warning found
-- Warning: --------------------------------------------------------------------
1 |class A { protected[this] def x = 2 }
  |                          ^
  |The [this] qualifier will be deprecated in the future; it should be dropped.
  |See: https://docs.scala-lang.org/scala3/reference/dropped-features/this-qualifier.html

初期化されてない var の値を表現するのに _ が非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> var a: String = _
1 warning found
-- Warning: --------------------------------------------------------------------
1 |var a: String = _
  |                ^
  |   `= _` has been deprecated; use `= uninitialized` instead.
  |   `uninitialized` can be imported with `scala.compiletime.uninitialized`.
var a: String = null

型として with を使うの非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> trait A; trait B
// defined trait A
// defined trait B
                                                                                                                                             
scala> def x: A with B = new A with B {}
1 warning found
-- [E003] Syntax Warning: ------------------------------------------------------
1 |def x: A with B = new A with B {}
  |         ^^^^
  |         with as a type operator has been deprecated; use & instead
  |
  | longer explanation available when compiling with `-explain`
def x: A & B
                                                                                                                                             
scala> def x: A & B = new A with B {}
def x: A & B

可変長引数に対しての古い記法が非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> val xs = (1 to 10)
val xs: scala.collection.immutable.Range.Inclusive = Range 1 to 10
                                                                                                                                             
scala> List(xs : _*)
1 warning found
-- Warning: --------------------------------------------------------------------
1 |List(xs : _*)
  |          ^
  |The syntax `x: _*` is no longer supported for vararg splices; use `x*` instead
val res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                                                                                                                                             
scala> List(xs *)
val res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

eta expansion時の _ 付与が非推奨

Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> def x = identity[Int] _
1 warning found
-- Warning: --------------------------------------------------------------------
1 |def x = identity[Int] _
  |        ^^^^^^^^^^^^^^^
  |        The syntax `<function> _` is no longer supported;
  |        you can simply leave out the trailing ` _`
def x: Int => Int
                                                                                                                                             
scala> def x = identity[Int]
def x: Int => Int

infixで定義してないメソッドをinfix記法で呼ぶと警告

  • Scala 3からinfix defという定義ができるようになっていましたが、それが本格運用される的な
  • infixとは日本語だと中置記法、以下のようにドット . と括弧 ( ) を省略できる記法です
  • つまり obj.method(arg)obj method arg と書ける記法
  • infix def で定義する、定義されてないならinfixの記法で呼ぶのやめる(括弧とドットを付与して普通に呼ぶ)、バッククオートで囲う、のいずれかをしましょう
  • -Xsource:3 指定すればScala 2でも infix def 自体の記法は許可されます
Welcome to Scala 3.4.0-RC1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> object A {
     |   def foo(x: Int): Int = x + 3
     |   infix def bar(x: Int): Int = x + 10
     | }
// defined object A
                                                                                                                                             
scala> A foo 2
1 warning found
-- Warning: --------------------------------------------------------------------
1 |A foo 2
  |  ^^^
  |Alphanumeric method foo is not declared infix; it should not be used as infix operator.
  |Instead, use method syntax .foo(...) or backticked identifier `foo`.
val res0: Int = 5
                                                                                                                                             
scala> A.foo(2)
val res1: Int = 5
                                                                                                                                             
scala> A `foo` 2
val res2: Int = 5
                                                                                                                                             
scala> A bar 2
val res3: Int = 12

for式でパターンマッチしてる場合に case がないとエラーに

3.3なら、デフォルトはまだ警告

Welcome to Scala 3.3.1 (11.0.21, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> for { (1, a) <- Option((2, "aaa")) } yield a
1 warning found
-- Warning: --------------------------------------------------------------------
1 |for { (1, a) <- Option((2, "aaa")) } yield a
  |       ^
  |pattern's type (1 : Int) is more specialized than the right hand side expression's type Int
  |
  |If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
  |which will result in a filtering for expression (using `withFilter`).
val res0: Option[String] = None

3.4だとエラー

Welcome to Scala 3.4.0-RC1 (1.8.0_392, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                             
scala> for { (1, a) <- Option((2, "aaa")) } yield a
-- Error: ----------------------------------------------------------------------
1 |for { (1, a) <- Option((2, "aaa")) } yield a
  |       ^
  |pattern's type (1 : Int) is more specialized than the right hand side expression's type Int
  |
  |If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
  |which will result in a filtering for expression (using `withFilter`).
1 error found

scala> for { case (1, a) <- Option((2, "aaa")) } yield a // case付与すると通る
val res0: Option[String] = None

追記