Scala3の新しい制御と静かな構文
1. 序章
Scala 3は、いわゆる「静かな構文」を追加しました。これにより、条件とループで中括弧と括弧の数を減らすことができます。 このチュートリアルでは、Scala 3での静かな構文の使用例と、比較のためのScala2に相当するものをいくつか紹介します。
2. 重要なインデント
重要なインデント、Python、により、フロー制御に使用される中括弧の数を減らすことができます。 Scala 3のquiet構文を使用したメソッド定義の例と、それに続くScala2の対応する定義を見てみましょう。
def luckyNumberScala3(): Int =
println("Returning lucky number...")
7
def luckyNumberScala2(): Int = {
println("Returning lucky number...")
7
}
ご覧のとおり、インデントは中括弧の使用に置き換えられます。中括弧は、静かな構文では完全に削除されます。 後に行を分割して、静かな構文インデントを利用できるさまざまなトークンを確認してみましょう。
= => ? => <- catch do else finally for if match return then throw try while yield
新しい構文で一致式を書いてみましょう。
7 match // Scala 3
case 7 => "Lucky number"
case _ => "Non lucky number"
7 match { // Scala 2
case 7 => "Lucky number"
case _ => "Non lucky number"
}
クラス、特性、およびオブジェクトを作成するときに、静かな構文を使用することもできます。
case class AClassScala3():
def doSomething(): Unit = ???
object AClassScala3:
def apply(): AClassScala3 = ???
trait ATraitScala3:
def doSomethingElsee(): Unit
case class AClassScala2() {
def doSomething(): Unit = ???
}
object AClassScala2 {
def apply(): AClassScala2 = ???
}
trait ATraitScala2 {
def doSomethingElsee(): Unit
}
部分関数も新しい構文の恩恵を受けます。
val positiveIntScala3: Int => Int =
case x if x > 0 => x
val positiveIntScala2: Int => Int = {
case x if x > 0 => x
}
Scala 3はインデント用のタブとスペースの両方をサポートしますが、ただし、 1つを選択し、コードで一貫性を保つことをお勧めします。
3. 簡略化されたループと条件文
ループと条件文は、改訂された構文も利用できます。 違いを確認するために、いくつかの例を見てみましょう。
for i <- 0 until 1 do println(i) // Scala 3
for (i <- 0 until 1) println(i) // Scala 2
if 5 % 2 == 0 then println("even") // Scala 3
else println("odd")
if (5 % 2 == 0) println("even") // Scala 2
else println("odd")
新しいキーワードは、インライン条件ステートメントの括弧を実行してから置き換えます。 新しい構文では、次の場合のように、入力が少なくなるとは限りません。
最後に、forの理解を見てみましょう。
for i <- 0 until 5 // Scala 3
if i % 2 == 1
iWasOdd = 2 * i
yield iWasOdd
for {
i <- (0 until 5) // Scala 2
if (i % 2 == 1)
iWasOdd = 2 * i
} yield iWasOdd
上記の場合、同じコードを、おそらくより明確に、11.25% fより少ない文字で記述しました。 悪くない、全く!
4. 新しいインポートスタイル
インポートも変更されました。 いくつかの例を比較してみましょう。
import scala.collection.* // Scala 3
import scala.collection._ // Scala 2
import scala.collection.mutable.Map as MMap // Scala 3
import scala.collection.mutable.{Map => MMap} // Scala 2
import java.util.{Random as _, *} // Scala 3
import java.util.{Random => _, _} // Scala 2
上記の例から観察する主な違いは、次のとおりです。
- *は_を置き換えます
- キーワード「as」は=>に置き換わり、中括弧は不要になりました
- _は引き続きインポートを非表示にするために使用されます、ただし、最後の例は、単一のインポートを非表示にするために*と組み合わせて使用する方法を示しています
5. 新しい構文への移行
新しい構文に手動で移行することは、実際には実用的ではありません。 Scalaコンパイラには、新しい構文への全体的または部分的な移行を可能にするいくつかの新機能があります。
単純なケースクラスを移行してみましょう。
$ cat IWasScala2.scala
case class IWasScala2() {
def doStuff: Unit = ???
}
$ scalac -indent -rewrite IWasScala2.scala
[patched file IWasScala2.scala]
$ cat IWasScala2.scala
case class IWasScala2():
def doStuff: Unit = ???
コンパイラには、移行または特定の構文の適用に使用できる4つの新しいパラメータがあります(詳細については、コンパイラのドキュメントを確認してください)。
- -new-syntax :制御式に新しい制御構文( thenおよびdo)を適用します
- -old-syntax :条件の前後に括弧の使用を強制します
- -インデント:重要なインデントの使用を許可します
- -インデントなし:重要でないインデントを強制します
古いクラスに新しい構文を適用して、何が起こるかを確認できます。
$ cat DoError.scala
case class DoError() {
def doStuff: Unit = { if (2 > 0) { println("ok") } }
}
$ scalac -new-syntax DoError.scala
-- Error: DoError.scala:2:29 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2 | def doStuff: Unit = { if (2 > 0) { println("ok") } }
| ^^^^^^^
| This construct is not allowed under -new-syntax.
| This construct can be rewritten automatically under -new-syntax -rewrite -source 3.0-migration.
1 error found
$ scalac -new-syntax -rewrite DoError.scala
[patched file DoError.scala]
$ cat DoError.scala
case class DoError() {
def doStuff: Unit = { if 2 > 0 then { println("ok") } }
}
構文エラーはコンパイラーによって明確にマークされ、新しい構文に自動的に移行する方法に関する提案もメッセージで報告されます。
6. 結論
さまざまな例で見たように、場合によっては、新しい構文の方が表現力が高く、入力を節約できます。 ただし、これが常に当てはまるとは限りません。 時々それはただの習慣の問題です。 ただし、繰り返し述べたように、コードベース全体で一貫性を維持することが重要です。
いつものように、この記事に記載されているすべてのコードは、GitHubでから入手できます。