2

考虑以下函数:

import java.util.concurrent.Callable;

def callable[T]( operation: =>T) : Callable[T] = {
  new Callable[T] {
    def call : T = operation
  }
}

在 REPL 中,此代码执行我想要的操作:

scala> val myCallable = callable {
     | println("Side effect!");
     | "Hi!"
     | }
myCallable: java.util.concurrent.Callable[String] = $anon$1@11ba4552

scala> myCallable.call
Side effect!
res3: String = Hi!

scala> myCallable.call
Side effect!
res4: String = Hi!

在调用函数'call'之前不会评估by-name参数,并且每次调用该函数时都会重新评估。这就是我想要的行为。

但在规范中,它对按名称参数进行了以下说明:

“相应的参数不会在函数应用时进行评估,而是在函数内的每次使用时进行评估。”

从这个描述中,我不清楚我可以依赖我想要的行为。“在函数内使用”是什么意思?我怎么知道这是指调用我的 Callable 的点(有时在不确定的将来),而不是定义它的点(非常“在函数内”)?

代码正在做我想做的事。但是,如果我确定这种行为是可靠的,而不是可能在某些未来版本的 scala 中“修复”的错误,我会更容易休息。

4

4 回答 4

2

这不是错误 - 行为符合预期。您可以将“在函数中的每次使用时评估”递归地视为“在评估该表达式时在函数中的表达式中的每次使用时评估”。

于 2013-03-26T12:34:47.103 回答
2

“函数”是您将参数传递给的函数。这段话试图警告你的是:

scala> def byName(arg: => String) = arg + arg
byName: (arg: => String)java.lang.String

scala> byName({println("hi") ; "foo" })
hi
hi
res0: java.lang.String = foofoo

即每次引用参数时都会发生副作用。由于您只执行一次,因此与您的情况无关(评估点除外,它在函数内部,而不是在调用站点)。

于 2013-03-26T12:40:38.683 回答
1

要扩展先前的答案并阐明避免这种情况的方法,如果您只想对其进行一次评估,则可以在函数内部的 val 中捕获该值。通过这样做,您将导致对“按名称”参数进行评估,并且不止一次地使用计算值,而不是导致对同一表达式进行 2 次评估。

scala> def byName(arg: => String) = {val computedArg = arg; computedArg + computedArg}
byName: (arg: => String)java.lang.String

scala> byName({"println("hi") ; "foo" })
hi
res0: java.lang.String = foofoo

如果您将来需要这样做...

于 2013-03-26T12:47:20.980 回答
0

最后,要对方法参数进行真正的惰性评估(仅零或一次评估),您可以这样做:

def m1(i: => Int) = {
  lazy val li = i
  ...
  // any number of static or dynamic references to li
  ...
}
于 2013-03-26T13:58:12.783 回答