自動 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
)- 如果預期類型為
() => T
的形式,我們會 eta 展開。 - 如果 m 是由 Java 定義,或覆寫由 Java 定義的方法,我們會插入
()
。 - 否則,我們會發出以下形式的錯誤:
方法必須以 () 引數呼叫
- 如果預期類型為
因此,當預期函數類型時,一個沒有傳入參數清單的未套用方法才會轉換為函數。建議的最佳實務做法是,明確地將方法套用至 ()
,或使用 () => m()
將其轉換為函數。
方法值語法 m _
已棄用。
參考
如需更多資訊,請參閱 PR #2701。
在本文中