将函数作为大括号之间的代码块传递

42

我曾几次看到这样的Scala代码:

object Doer{
   def doStuff(op: => Unit) {
      op
   }
}

以这种方式调用:

Doer.doStuff{
      println("Done")
}

对我来说奇怪的是,一个函数被传递到另一个函数,只作为花括号之间的一块代码。甚至没有括号来标记参数列表的开始和结束。

这种Scala语法/特性的名称是什么?我可以在什么情况下使用它?它在哪里有文档记录?


1
我认为这只是一种语法糖。圆括号不在这里,因为如果函数只有一个参数,可以省略圆括号。花括号内的代码片段是提供匿名函数的方式。 - Ashalynd
1
@Ashalynd 不行。你可以调用 f(args) 或者 f {block},但是除了中缀表达式 x op y 之外,不能省略标点符号。该块是一个匿名函数,只能写成 { case x => } - som-snytt
1个回答

55

这被称为 nullary function 或者 thunk,是一种按名称调用评估的示例:http://www.scala-lang.org/old/node/138

您可以在任何需要参数列表的地方使用 nullaries。它们基本上只是围绕零参数函数的语法糖,使它们看起来像普通值,并在引用它们时调用。

所以

def doSomething(op: => Unit) {
  op
}
doSomething {
  println("Hello!")
}

与以下内容完全相同:

def doSomething(op: () => Unit) {
  op()
}
doSomething(() => println("Hello!"))

在处理空参数函数时需要牢记一点,即每次引用该函数时都会调用它,因此以下代码:

def foo(op: => Int) = op + op
foo {
  println("foo")
  5
}

将会打印两次 "foo"。

编辑:根据Randall的评论,空元函数与零参数函数之间的一个重要区别是,空元函数不是一等值。例如,您可以有一个List[() => Int]但您不能有一个List[=> Int]。如果您有像下面这样的内容:

def foo(i: => Int) = List(i)

你没有将零元函数添加到列表中,只添加了它的返回值。


5
最好不要将"thunks"视为函数。就其本质而言,你不能将"thunk"视为可存储或传递的“Function”类型值,也不能像处理函数一样处理"thunk"。你只能触发实际参数表达式(可以是任意数量)的求值。 - Randall Schulz
为了澄清这个注释,你可以这样做:def f(op: =>Int) = op _,还有 def g(op: =>Int) = f(op),所以 g(8) 不会被评估。换句话说,按名称调用的参数是一个无参方法类型,就像 def x - som-snytt
@RandallSchulz,实际上这不是一个比喻,而是规范在3.3.1中定义的=>A的精确方式,它被评估每次引用时都是一个表达式,就像按名称传递的参数一样。(对我来说,这帮助我不去考虑它是如何实现或编码的。) - som-snytt
1
为什么很难找到任何官方文档呢?! - Piotr Sobczyk
我在这里找到了有关call-by-name的一些信息:http://www.scala-lang.org/old/node/138。我还会将链接添加到我的答案中。 - Dan Simon
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接