2011-10-07 59 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