Scala 3 遷移指南

手動移植 sbt 專案

語言

本教學課程是為 sbt 編寫的。然而,只要它支援 Scala 3,此方法對任何其他建置工具而言都非常類似。

在跳轉到 Scala 3 之前,請確認您使用的是最新的 Scala 2.13.x 和 sbt 1.5.x 版本。

現在讓我們逐步了解將整個專案移植到 Scala 3 所需的步驟。

1. 檢查專案先決條件

確認您的專案已準備好進行移植

  • 它不得依賴尚未移植到 Scala 3 的巨集函式庫。
  • 它不得使用在 Scala 3 中沒有等效項的編譯器外掛程式。
  • 它不得依賴 scala-reflect

這些先決條件在 前一頁 中有更詳細的說明。

2. 選擇模組

由於 Scala 2.13 和 Scala 3 之間的互通性,您可以從任何模組開始。但是,從依賴項最少的模組開始可能會比較簡單。

如果您在內部使用巨集定義或巨集註解,則必須先移植它們。

3. 設定跨建置

程式碼庫遷移的兩個主要挑戰為

  • 讓程式碼編譯
  • 確保執行時期行為不變

我們建議採用交叉建置策略,也就是使用 Scala 3 和 Scala 2.13 編譯程式碼。背後的邏輯是,在每次修正後都能使用 Scala 2.13 執行測試,進而確保執行時期行為不變。這對於避免在修正不相容性時可能發生的錯誤至關重要。

在 sbt 中設定交叉建置非常簡短,如下所示

scalaVersion := "3.3.1"
crossScalaVersions ++= Seq("2.13.11", "3.3.1")

此設定表示

  • 預設版本為 3.3.1
  • 執行 ++2.13.11 指令可載入 2.13.11。
  • 執行 ++3.3.1 指令可載入 3.3.1。

請注意,reload 指令將永遠載入預設版本,在此為 3.3.1。

4. 準備相依性

在此階段,如果您執行 compile,sbt 很可能會抱怨找不到某些相依性。這是因為相依性的宣告版本未針對 Scala 3 發布。

您需要將相依性升級到較新版本,或告訴 sbt 使用該函式庫的 Scala 2.13 版本。

當您變更函式庫相依性時,請務必在專案的所有模組中套用相同的變更。

檢查是否有可用的函式庫 Scala 3 版本。為此,您可以在 Scaladex 中使用版本矩陣。前往函式庫的專案頁面,按一下版本矩陣按鈕,篩選 Scala 3 和 Scala 2.13。

1. 有函式庫的 Scala 3 版本

我們強烈建議使用其中一個可用版本。請確保您選擇的版本不會帶來任何重大變更。

2. 沒有函式庫的 Scala 3 版本

您可以使用函式庫的 Scala 2.13 版本。語法如下

("com.lihaoyi" %% "os-lib" % "0.7.7").cross(CrossVersion.for3Use2_13)

或對於 Scala.js 相依性

("com.lihaoyi" %%% "os-lib" % "0.7.7").cross(CrossVersion.for3Use2_13)

修正所有未解析的相依性後,您可以檢查測試是否仍在 Scala 2.13 中通過

sbt:example> ++2.13.11
[info] Setting Scala version to 2.13.11 on 1 project.
...
sbt:example> example / test
...
[success]

5. 設定 Scala 3 編譯器

Scala 3 編譯器選項與 Scala 2.13 的選項不同:有些已重新命名,有些尚未支援。您可以參閱 編譯器選項查詢 頁面,以調整 scalacOptions 清單至 Scala 3。

您應該提出一個常見選項清單、一個 Scala 2.13 特定選項清單和一個 Scala 3 特定選項清單。

典型的設定如下所示

scalacOptions ++= {
  Seq(
    "-encoding",
    "UTF-8",
    "-feature",
    "-language:implicitConversions",
    // disabled during the migration
    // "-Xfatal-warnings"
  ) ++ 
    (CrossVersion.partialVersion(scalaVersion.value) match {
      case Some((3, _)) => Seq(
        "-unchecked",
        "-source:3.0-migration"
      )
      case _ => Seq(
        "-deprecation",
        "-Xfatal-warnings",
        "-Wunused:imports,privates,locals",
        "-Wvalue-discard"
      )
    })
}

加入 -source:3.0-migration 選項,以開啟 Scala 3 遷移模式
您還應該停用 -Xfatal-warnings,以充分利用遷移模式和自動重寫。

6. 解決不相容性

現在是嘗試使用 Scala 3 編譯的時候了

sbt:example> ++3.3.1
[info] Setting Scala version to 3.3.1 on 1 project.
...
sbt:example> example / compile
...
sbt:example> example / Test / compile

example / compile 編譯範例專案的 main 來源。它與 example / Compile / compile 完全相同。

example / Test / compile 編譯 test 來源。

編譯器產生兩個不同層級的診斷

  • 遷移警告:這些警告可以用 -rewrite 選項讓編譯器自動修復。
  • 錯誤:一段程式碼無法再編譯。

你可以忽略遷移警告,因為編譯器會自動修復它們。但是不相容性錯誤必須手動處理。

許多已知的不相容性列在 不相容性表格 中。你可以在這裡找到錯誤的描述和一些建議的解決方案。

如果可能,你應該嘗試找到一個最能保留程式碼二進位相容性的修復程式。如果你的專案是一個已發布的函式庫,這一點尤其重要。

巨集不相容性無法輕易解決。必須從頭開始重寫大量程式碼。請參閱 巨集程式設計

修復不相容性後,你可以透過在 Scala 2.13 中執行測試來驗證解決方案。

sbt:example> ++2.13.11
[info] Setting Scala version to 2.13.11 on 1 project.
...
sbt:example> example / test
...
[success]

考慮定期提交你的變更。

修復所有錯誤後,你應該能夠在 Scala 3 中成功編譯。只有遷移警告仍然存在。你可以透過使用 -source:3.0-migration -rewrite 選項編譯來自動修復它們。

sbt:example> ++3.3.1
sbt:example> set example / scalacOptions += "-rewrite"
sbt:example> example / compile
...
[info] [patched file /example/src/main/scala/app/Main.scala]
[warn] two warnings found
[success]

現在你應該移除 -source:3.0-migration 選項,你也可以再次新增 -Xfatal-warnings 選項。不要忘記重新載入。

7. 驗證遷移

在罕見的情況下,不同的隱含值可能會被解析,並改變程式的執行期行為。良好的測試是防止此類錯誤不被注意到的唯一保證。

確保測試在 Scala 2.13 和 Scala 3 中都能通過。

sbt:example> ++2.13.11
sbt:example> example / test
...
[success]
sbt:example> ++3.3.1
sbt:example> example / test
...
[success]

如果您有持續整合管道,現在是為 Scala 3 設定它的時候了。

8. 完成遷移

恭喜!您已成功將模組移植到 Scala 3。可以對每個模組重複相同的程序,直到專案完全遷移到 Scala 3。

您可以保留或刪除 Scala 2.13 交叉建置設定,具體取決於您是否要交叉發佈您的程式。

到此,我們完成 sbt 專案遷移的說明。

此頁面的貢獻者