2011-10-07 96 views
6

在下面的代碼示例中,我不明白爲什麼fun函數可以作爲參數傳遞給方法addAction。方法fun的類型爲Unit,而方法addAction預計類型爲() => Unit的函數。Scala方法類型和方法作爲參數

如果fun() => Unit型的,那麼,爲什麼編譯器抱怨funUnit類型的,當我嘗試添加fun到操作列表:actions = fun :: actions

package myscala 

object MyScala { 

    def fun() { println("fun1 executed.") } 

    def addAction(a:() => Unit) { 
    actions = a :: actions 
    } 

    var actions: List[() => Unit] = List() 

    def main(args: Array[String]) { 
    // the following line would produce a compiler error (found: Unit, required:() => Unit), it's OK 
    // actions = fun :: actions 
    actions = (() => fun) :: actions // OK 
    // I would expect the same compiler error here (found: Unit, required:() => Unit), but it's OK why? 
    addAction(fun) 
    actions.foreach(_()) // prints twice "fun1 executed" 
    } 
} 

回答

8

以此爲入門例如:

def fun() { println("fun1 executed.") } 

val a1 = fun 
val a2:() => Unit = fun 

兩條線編譯(感謝類型推斷),他們看起來相當的。然而a1的類型是Unita2() => Unit的類型......這怎麼可能?

由於您沒有明確提供的a1類型,編譯器解釋fun爲方法fun呼叫Unit類型的,因此的a1類型相同類型的fun。這也意味着這行會打印fun1執行。

但是,a2已明確聲明類型() => Unit。編譯器可以幫助你,它理解由於上下文需要() => Unit類型的函數,並且你提供了一個匹配這個類型的方法,所以它不應該調用這個方法,而應該把它當作第一類函數!

您並非註定要明確指定a1的類型。說:

val a1 = fun _ 

你現在明白你的問題在哪裏?

+0

是的,我願意。這對我來說現在看起來很明顯(當時根本沒有)。當編譯器能夠推斷出一個函數類型的時候,我可以簡單地寫'fun',否則我必須明確地說我正在傳遞一個函數。感謝所有人的明確答覆! – Manu

+0

@Manu:考慮接受一個你認爲是最好的答案(不一定是這個答案) –

5

您需要在第一種情況下編寫fun _以避免調用方法並執行eta-expansion。

這將工作:

actions = (fun _) :: actions 

如果你不這樣做,那麼fun評估。

有關更多詳細信息,請參閱Scala Language Reference的第6.7節(方法值)。

至於爲什麼fun不在第二種情況下評估,這是因爲類型推斷可以清楚地得出結論addAction需要一個函數。順便說一下,fun的類型在技術上是()Unit,而不是Unit,即方法類型,而不是值類型。有關更多信息,請參見reference中的第3.3.1節。

+3

我寧願建議從輕閱讀:[編程在斯卡拉第9章](http://www.artima.com/pins1ed/control-abstraction.html) – Jamil

3

方法和函數是有區別的。在你的情況下,actions是一個函數列表。當編譯器知道需要某個函數時(例如addAction),它可以自動將方法fun轉換爲函數。現在::也是一種方法,因此編譯器也知道它將函數作爲參數。但問題是右聯合算子::的語法糖。如果你把它稱爲一種方法:actions.::(fun)它會編譯(雖然我目前無法測試它)。編寫fun :: actions時,編譯器認爲fun是一個表達式,因此對其進行求值並且因爲它「返回」Unit,所以會導致編譯器錯誤。

編輯

因爲我現在就來測試我的假設(這是錯誤的)這是你的選擇的可能性:

// Usual syntax 
actions.::[() => Unit](fun) 
actions.::(fun:() => Unit) 
actions.::(fun _) 
// Operator syntax 
(fun:() => Unit) :: actions 
(fun _) :: actions 
+0

'actions。::(fun)'計算到'List [Any] = List(())'。它仍然在評估功能。不知道爲什麼List [()=> Unit]轉換爲List [Any]?協方差? – Jamil

+2

是的,'List [Any]'是可以包含'Unit's和'()=> Unit's的列表的最精確類型。 ''''看作'()'是評估'fun'返回值。 – Philippe

+0

我編輯了我的答案。 – agilesteel