名字参数的模糊语义?

时间:2013-03-26 12:01:04

标签: scala

考虑以下功能:

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参数,并且每次调用该函数时都会重新评估该参数。这就是我想要的行为。

但在spec中,它说明了以下有关名字参数:

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

从这个描述中,我不清楚我可以依赖我想要的行为。 “在功能中使用意味着什么”是什么意思?我如何知道这是指我的Callable被调用的点(在无限期的某个时间),而不是它定义的点(非常“在函数内”)?

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

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

答案 3 :(得分:0)

最后,要获得方法参数的真正 lazy 评估(仅限零个或一个评估),您可以这样做:

def m1(i: => Int) = {
  lazy val li = i
  ...
  // any number of static or dynamic references to li
  ...
}