Scala 2.13 類型檢查器在某些特定情況下是不健全的。這可能會導致我們預料之外的地方發生令人驚訝的執行時期錯誤。Scala 3 基於更強大的理論基礎,類型檢查器中的這些不健全錯誤現在已修復。
變異檢查中的不健全修正
在 Scala 2 中,預設參數和內部類別不受變異檢查的約束。這是不健全的,可能會導致執行時期失敗,如 Scala 3 儲存庫中的 此測試 所示。
Scala 3 編譯器不再允許這樣做。
class Foo[-A](x: List[A]) {
def f[B](y: List[B] = x): Unit = ???
}
class Outer[+A](x: A) {
class Inner(y: A)
}
因此,如果您在 Scala 3 中編譯,您將會收到以下錯誤。
-- Error: src/main/scala/variance.scala:2:8
2 | def f[B](y: List[B] = x): Unit = y
| ^^^^^^^^^^^^^^^^^
|contravariant type A occurs in covariant position in type [B] => List[A] of method f$default$1
-- Error: src/main/scala/variance.scala:6:14
6 | class Inner(y: A)
| ^^^^
|covariant type A occurs in contravariant position in type A of parameter y
此類型的每個問題都需要特定的處理。您可以逐案嘗試下列選項
- 將類型
A
設定為不變 - 在類型參數
B
上新增下界或上界 - 新增新的方法重載
在我們的範例中,我們可以選擇這兩個解決方案
class Foo[-A](x: List[A]) {
- def f[B](y: List[B] = x): Unit = ???
+ def f[B](y: List[B]): Unit = ???
+ def f(): Unit = f(x)
}
class Outer[+A](x: A) {
- class Inner(y: A)
+ class Inner[B >: A](y: B)
}
或者,作為暫時解決方案,您也可以使用 uncheckedVariance
註解
class Outer[+A](x: A) {
- class Inner(y: A)
+ class Inner(y: A @uncheckedVariance)
}
模式比對中的不健全修正
Scala 3 修復了模式比對中的一些不健全錯誤,防止某些語義錯誤的比對表達式進行類型檢查。
例如,combineReq
中的比對表達式可以使用 Scala 2.13 編譯,但無法使用 Scala 3 編譯。
trait Request
case class Fetch[A](ids: Set[A]) extends Request
object Request {
def combineFetch[A](x: Fetch[A], y: Fetch[A]): Fetch[A] = Fetch(x.ids ++ y.ids)
def combineReq(x: Request, y: Request): Request = {
(x, y) match {
case (x @ Fetch(_), y @ Fetch(_)) => combineFetch(x, y)
}
}
}
在 Scala 3 中,錯誤訊息為
-- [E007] Type Mismatch Error: src/main/scala/pattern-match.scala:9:59
9 | case (x @ Fetch(_), y @ Fetch(_)) => combineFetch(x, y)
| ^
| Found: (y : Fetch[A$2])
| Required: Fetch[A$1]
這是正確的,沒有證據證明 x
和 y
具有相同的類型參數 A
。
從 Scala 2 的角度來看,這顯然是一種改進,有助於我們找出程式碼中的錯誤。要解決這種不相容性,最好找到一種可以由編譯器檢查的解決方案。這並不總是容易的,有時甚至是不可能的,在這種情況下,程式碼很可能會在執行時失敗。
在此範例中,我們可以放寬對 x
和 y
的約束,指出 A
是兩個類型參數的共同祖先。這使編譯器可以成功對程式碼進行類型檢查。
或者,一個通用的但並不安全的解決方案是進行強制轉換。