Scala 3 透過新的控制結構和重要的縮排語法擴充了 Scala 語言的語法。兩者都是選用的,因此 Scala 2 程式碼風格在 Scala 3 中仍然完全有效。
控制結構的新語法可以撰寫 if
運算式的條件、while
迴圈的條件或 for
運算式的產生器,而不用加上括號。
重要的縮排語法讓大括號 {...}
在許多情況下都不需要:類別和方法主體、if
運算式、match
運算式等等。您可以在 Scala 3 參考網站的 選用大括號 頁面中找到完整的說明。
手動將現有的 Scala 程式碼轉換為新的語法既繁瑣又容易出錯。在本章中,我們將展示如何使用編譯器自動將您的程式碼從經典的 Scala 2 風格重寫為新的風格,反之亦然。
語法重寫選項
讓我們先展示一下我們可用的編譯器選項,以達成我們的目標。如果我們只在命令列中輸入 scalac
,它會列印出我們可以使用的所有選項。對於我們的目的,我們將使用以下五個選項
$ scalac
Usage: scalac <options> <source files>
where possible standard options include:
...
-indent</b> Allow significant indentation
...
-new-syntax</b> Require `then` and `do` in control expressions.
-no-indent</b> Require classical {...} syntax, indentation is not significant.
...
-old-syntax</b> Require `(...)` around conditions.
...
-rewrite</b> When used in conjunction with a `...-migration` source version,
rewrites sources to migrate to new version.
...
前四個選項中的每一個都對應到一個特定的語法
語法 | 選項 |
---|---|
新的控制結構 | -new-syntax |
舊的控制結構 | -old-syntax |
語法 | 編譯器選項 |
---|---|
顯著縮排 | -indent |
傳統大括號 | -noindent |
我們將在後續詳細說明,這些選項可與 -rewrite
選項結合使用,以自動轉換為特定語法。讓我們看看這在一個小範例中如何運作。
新的語法重寫
給定以下以 Scala 2 樣式撰寫的原始程式碼。
case class State(n: Int, minValue: Int, maxValue: Int) {
def inc: State =
if (n == maxValue)
this
else
this.copy(n = n + 1)
def printAll: Unit = {
println("Printing all")
for {
i <- minValue to maxValue
j <- 0 to n
} println(i + j)
}
}
我們將能夠透過兩個步驟自動將其移至新的語法:首先使用新的控制結構重寫 (-new-syntax -rewrite
),然後使用顯著縮排重寫 (-indent -rewrite
)。
-indent
選項不適用於傳統控制結構。因此,請務必按正確順序執行兩個步驟。
遺憾的是,編譯器無法同時套用兩個步驟:
-indent -new-syntax -rewrite
。
新的控制結構
我們可以透過將 -new-syntax -rewrite
選項新增到建置工具中 scalac 選項清單來使用它們。
// build.sbt, for Scala 3 project
scalacOptions ++= Seq("-new-syntax", "-rewrite")
編譯程式碼後,結果如下所示
case class State(n: Int, minValue: Int, maxValue: Int) {
def inc: State =
if n == maxValue then
this
else
this.copy(n = n + 1)
def printAll: Unit = {
println("Printing all")
for
i <- minValue to maxValue
j <- 0 to n
do println(i + j)
}
}
請注意,n == maxValue
周圍的括號消失了,i <- minValue to maxValue
和 j <- 0 to n
產生器的周圍大括號也消失了。
顯著縮排語法
在第一次重寫之後,我們可以使用顯著縮排語法來移除剩餘的大括號。為此,我們將 -indent
選項與 -rewrite
選項結合使用。這將引導我們到以下版本
case class State(n: Int, minValue: Int, maxValue: Int):
def inc: State =
if n == maxValue then
this
else
this.copy(n = n + 1)
def printAll: Unit =
println("Printing all")
for
i <- minValue to maxValue
j <- 0 to n
do println(i + j)
移回傳統語法
從我們程式碼範例的最新狀態開始,我們可以向後移回其初始狀態。
讓我們在保留新控制結構的同時使用大括號重寫程式碼。在使用 -no-indent -rewrite
選項編譯後,我們將取得以下結果
case class State(n: Int, minValue: Int, maxValue: Int) {
def inc: State =
if n == maxValue then
this
else
this.copy(n = n + 1)
def printAll: Unit = {
println("Printing all")
for {
i <- minValue to maxValue
j <- 0 to n
}
do println(i + j)
}
}
再重寫一次,使用 -old-syntax -rewrite
,將我們帶回原始的 Scala 2 樣式程式碼。
有了最後一次的重寫,我們已經完成了整個循環。
在循環使用語法版本時格式遺失
當使用格式化工具(例如 scalafmt)將自訂格式套用至您的程式碼時,在不同的 Scala 3 語法變體之間來回循環可能會在完成整個循環時造成差異。
強制執行特定語法
可以在單一程式碼庫中混合舊語法和新語法。雖然我們建議不要這麼做,因為這會降低可讀性並使程式碼更難維護。更好的方法是選擇一種樣式,並將其一致地套用至整個程式碼庫。
-no-indent
、-new-syntax
和 -old-syntax
可用作獨立選項,以強制執行一致的語法。
例如,使用 -new-syntax
選項時,編譯器會在遇到 if
條件周圍的括號時發出錯誤。
-- Error: /home/piquerez/scalacenter/syntax/example.scala:6:7 ------------------
6 | if (n == maxValue)
| ^^^^^^^^^^^^^^^
|This construct is not allowed under -new-syntax.
|This construct can be rewritten automatically under -new-syntax -rewrite -source 3.0-migration.
-indent
語法始終是選用的,編譯器無法強制執行。