Scala 3 遷移指南

Kind Projector 遷移

語言

未來,Scala 3 將使用 _ 底線符號作為類型 lambda 中的佔位符,就像目前在(一般)項級 lambda 中使用底線作為佔位符一樣。

新的類型 lambda 語法並未預設啟用,若要啟用,請使用編譯器標記 -Ykind-projector:underscores。請注意,啟用底線類型 lambda 會停用 _ 作為萬用字元的使用,您只能使用 ? 符號撰寫萬用字元。

如果您希望為 Scala 2 和 Scala 3 交叉編譯專案,同時對兩者都使用底線類型 lambda,您可以從 kind-projector 版本 0.13.0 以上和 Scala 2 版本 2.13.62.12.14 開始執行此操作。若要啟用,請將編譯器標記 -Xsource:3 -P:kind-projector:underscore-placeholders 新增到您的建置。與 Scala 3 一樣,這會停用 _ 作為萬用字元的使用,但標記 -Xsource:3 將允許您使用 ? 符號取代它。

以下 sbt 組態將設定正確的旗標以使用新語法進行跨編譯

ThisBuild / scalacOptions ++= {
  CrossVersion.partialVersion(scalaVersion.value) match {
    case Some((3, _)) => Seq("-Ykind-projector:underscores")
    case Some((2, 12 | 13)) => Seq("-Xsource:3", "-P:kind-projector:underscore-placeholders")
  }
}

移轉到新語法

要在已啟用 kind-projector 的現有程式碼中使用底線作為類型 lambda,請將 *? 類型 lambda 佔位符替換為 _

反過來,您還必須將 _ 作為萬用字元的所有用法改寫為使用 ? 符號。

例如,萬用字元的以下用法

def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) 

必須改寫為

def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) 

以及 kind-projector 的 * 佔位符的以下用法

Tuple2[*, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

必須改寫為

Tuple2[_, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +_]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-_, Long, +_]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

編譯現有程式碼

即使不移轉到底線類型 lambda,您可能仍可以在沒有變更的情況下使用 Scala 3 編譯大部分程式碼。

使用旗標 -Ykind-projector 來啟用對 * 為基礎的類型 lambda 的支援(不啟用底線類型 lambda),以下形式現在將編譯

Tuple2[*, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

改寫不相容的結構

Scala 3 的 -Ykind-projector-Ykind-projector:underscores 僅實作 kind-projector 語法的子集,特別是它們不實作

  • 高階類型 lambda 佔位符
  • 高階命名類型 lambda 參數
  • Lambda 關鍵字(λ 仍受支援)

您必須改寫以下所有形式

// classic
EitherT[*[_], Int, *]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// underscores
EitherT[_[_], Int, _]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// named λ
λ[(F[_], A) => EitherT[F, Int, A]]
// named Lambda
Lambda[(F[_], A) => EitherT[F, Int, A]]

成以下長格式才能使用 Scala 3 進行跨編譯

type MyLambda[F[_], A] = EitherT[F, Int, A]
MyLambda

或者,如果您不需要進行跨編譯,可以使用 Scala 3 的 原生類型 lambda

[F[_], A] =>> EitherT[F, Int, A]

對於 Lambda,您必須改寫以下形式

Lambda[(`+E`, `+A`) => Either[E, A]]

成以下形式才能進行跨編譯

λ[(`+E`, `+A`) => Either[E, A]]

或者改成 Scala 3 類型 lambda

[E, A] =>> Either[E, A]

注意:Scala 3 類型 lambda 不再需要參數上的 -+ 變異標記,這些標記現在會推論。

此頁面的貢獻者