在 GitHub 上編輯此頁面

自動 Eta 展開 - 更多詳情

動機

Scala 在方法函式之間維持方便的區別。方法是類別定義的一部分,可以在物件中呼叫,而函式本身就是完整的物件,使其成為一級實體。例如,它們可以指定給變數。這兩種機制在 Scala 中透過稱為eta 擴充(也稱為 eta 抽象)的機制來橋接,它將方法的參考轉換為函式。直覺上,方法 m 可以透過將其轉換為物件來傳遞:函式 x => m(x)

在此範例中,將方法指定給 val,編譯器會執行自動 eta 擴充,如下面的註解所示

def m(x: Int, y: String) = ???
val f = m // becomes: val f = (x: Int, y: String) => m(x, y)

在 Scala 2 中,只有在預期類型為函數類型時,方法參考 m 才會轉換為函數值,這表示上述範例中的轉換不會觸發,因為 val f 沒有類型註解。若仍要進行 eta 展開,捷徑 m _ 會強制進行轉換。

對於如上述範例中具有一個或多個參數的方法,此限制現已取消。語法 m _ 不再需要,未來將會棄用。

自動 eta 展開和部分應用

在下列範例中,m 可以部分應用於前兩個參數。將 m 指定給 f1 會自動 eta 展開。

def m(x: Boolean, y: String)(z: Int): List[Int]
val f1 = m
val f2 = m(true, "abc")

這會建立兩個函數值

f1: (Boolean, String) => Int => List[Int]
f2: Int => List[Int]

自動 eta 展開和內隱參數清單

具有內隱參數清單的方法將永遠應用於內隱引數。

def foo(x: Int)(implicit p: Double): Float = ???
implicit val bla: Double = 1.0

val bar = foo // val bar: Int => Float = ...

自動 eta 展開和內容類型

具有內容參數的方法可以透過明確撰寫預期的內容類型,來擴充為內容類型的值。

def foo(x: Int)(using p: Double): Float = ???
val bar: Double ?=> Float = foo(3)

規則

  • 如果 m 具有包含一個或多個參數的引數清單,我們會永遠 eta 展開
  • 如果 m 具有空的引數清單(即具有類型 ()R
    1. 如果預期類型為 () => T 的形式,我們會 eta 展開。
    2. 如果 m 是由 Java 定義,或覆寫由 Java 定義的方法,我們會插入 ()
    3. 否則,我們會發出以下形式的錯誤:方法必須以 () 引數呼叫

因此,當預期函數類型時,一個沒有傳入參數清單的未套用方法才會轉換為函數。建議的最佳實務做法是,明確地將方法套用至 (),或使用 () => m() 將其轉換為函數。

方法值語法 m _ 已棄用。

參考

如需更多資訊,請參閱 PR #2701