Scala方法的类型和方法作为参数

时间:2011-10-07 17:38:19

标签: scala methods anonymous-function

在下面的代码示例中,我不明白为什么函数fun可以作为参数传递给方法addAction。方法fun的类型为Unit,而方法addAction需要类型为() => Unit的函数。

如果fun的类型为() => Unit,那么当我尝试将fun添加到Unit时,为什么编译器会抱怨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" } }

{{1}}

3 个答案:

答案 0 :(得分:8)

以此作为介绍性示例:

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

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

两行编译和(由于类型推断)它们看起来相同。但是,a1的类型为Unit,而a2的类型为() => Unit ...这怎么可能?

由于您未明确提供a1类型,因此编译器会将fun解释为类型为fun的方法Unit,因此类型为a1fun的类型相同。这也意味着该行将打印 fun1执行。

但是,a2已明确声明() => Unit的类型。编译器在这里帮助你,并且它理解由于上下文需要类型() => Unit的函数,并且你提供了一个匹配这种类型的方法,它不应该调用该方法,而是将它视为第一类函数!

您并非注定要明确指定a1的类型。话说:

val a1 = fun _

您现在了解问题所在吗?

答案 1 :(得分:5)

您需要在第一种情况下编写fun _以避免调用该方法并执行eta-expansion。

这将有效:

actions = (fun _) :: actions

如果您不这样做,则会评估fun

有关详细信息,请参阅Scala Language Reference的第6.7节(方法值)。

至于为什么fun未在第二种情况下进行评估,这是因为类型推断可以清楚地得出addAction期望函数的结论。顺便说一下,fun的类型在技术上是()Unit,而不是Unit,即方法类型,而不是值类型。有关详细信息,请参阅reference中的第3.3.1节。

答案 2 :(得分: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