Scala 3 遷移指南

情境抽象

語言

情境抽象 的重新設計帶來了一些不相容性。

不相容性 Scala 2.13 Scala 3 遷移重寫 Scalafix 規則 執行時期不相容性
隱式 def 的類型      
隱式檢視       可能
檢視界線 不建議使用      
A=> A 上有不明確的轉換        

隱式定義的類型

在 Scala 3 中,隱式定義的類型(valdef)需要明確給出。它們不能再被推斷出來。

名為 ExplicitResultTypes 的 Scalafix 規則可以自動撰寫遺失的類型註解。

隱式檢視

Scala 3 不支援從隱式函式值進行隱式轉換,形式為 implicit val ev: A => B

以下程式碼片段現在在 Scala 3 中無效

trait Pretty {
  val print: String
}

def pretty[A](a: A)(implicit ev: A => Pretty): String =
  a.print // In Scala 3, Error: value print is not a member of A

Scala 3 遷移編譯 可以警告您這些情況,但它不會嘗試修復它。

請注意,這種不相容性可能會產生執行時期不相容性並中斷您的程式。事實上,編譯器可以從更廣泛的範圍中找到另一個隱式轉換,這最終會導致執行時期出現不需要的行為。

此範例說明了這個情況

trait Pretty {
  val print: String
}

implicit def anyPretty(any: Any): Pretty = new Pretty { val print = "any" }

def pretty[A](a: A)(implicit ev: A => Pretty): String =
  a.print // always print "any"

已解析的轉換取決於編譯器模式

  • -source:3.0-migration:編譯器執行 ev 轉換
  • -source:3.0:編譯器無法執行 ev 轉換,但它可以執行 anyPretty,這是不可取的

在 Scala 3 中,一個簡單的解決方法是明確提供正確的轉換

def pretty[A](a: A)(implicit ev: A => Pretty): String =
-  a.print
+  ev(a).print

檢視邊界

檢視邊界已被棄用很長一段時間,但它們仍受 Scala 2.13 支援。它們無法再用 Scala 3 編譯。

def foo[A <% Long](a: A): Long = a

在此範例中,在 Scala 3 中,我們會收到以下錯誤訊息

-- Error: src/main/scala/view-bound.scala:2:12 
2 |  def foo[A <% Long](a: A): Long = a
  |            ^
  |          view bounds `<%' are deprecated, use a context bound `:' instead

訊息建議使用內容邊界取代檢視邊界,但它會變更方法的簽章。保留二進位相容性可能較容易且安全。為此,必須宣告隱式轉換並明確呼叫它。

小心不要陷入上述在 隱式檢視 中所述的執行時期不相容性。

-def foo[A <% Long](a: A): Long = a
+def foo[A](a: A)(implicit ev: A => Long): Long = ev(a)

A=> A 上的不明確轉換

在 Scala 2.13 中,在 A 上的隱式轉換會優先於在 => A 上的隱式轉換。在 Scala 3 中不再是如此,並導致不明確轉換。

例如,在此範例中

implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool:  => Boolean): Foo = ???

true.foo()

Scala 2.13 編譯器會選擇 boolFoo 轉換,但 Scala 3 編譯器無法編譯。

-- Error: src/main/scala/ambiguous-conversion.scala:4:19
9 |  true.foo()
  |  ^^^^
  |Found:    (true : Boolean)
  |Required: ?{ foo: ? }
  |Note that implicit extension methods cannot be applied because they are ambiguous;
  |both method boolFoo in object Foo and method lazyBoolFoo in object Foo provide an extension method `foo` on (true : Boolean)

暫時解決方法是明確撰寫轉換。

implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool:  => Boolean): Foo = ???

-true.foo()
+boolFoo(true).foo()

此頁面的貢獻者