如果方法是中缀和右关联的,为什么 Scala 会评估按名称调用参数的参数?

Why does Scala evaluate the argument for a call-by-name parameter if the method is infix and right-associative?

据我了解call-by-name方法的参数,相应的参数表达式在传递给方法时不会被计算,只有当(并且如果)参数的值在方法中使用时才会计算正文。

然而,在下面的例子中,这只在前两个方法调用中是正确的,而在第三个方法调用中不是,尽管它应该只是第二种情况的语法变体!?

为什么在第三个方法调用中计算参数表达式?

(我使用 Scala 2.11.7 测试了这段代码)

class Node(x: => Int)

class Foo {
  def :: (x: =>Int) = new Node(x)  // a right-associative method
  def !! (x: =>Int) = new Node(x)  // a left-associative method
}

// Infix method call will not evaluate a call-by-name parameter:
val node = (new Foo) !! {println(1); 1}
println("Nothing evaluated up to here")

// Right-associative method call will not evaluate a call-by-name parameter:
val node1 = (new Foo).::({println(1); 1})
println("Nothing evaluated up to here")

// Infix and right-associative method call will evaluate a call-by-name parameter - why??
val node2 = {println(1); 1} ::(new Foo)  // prints 1
println("1 has been evaluated now - why??")

2020 年编辑:请注意,Scala 2.13 不再显示这种恼人的行为:val node2 = ... 不再打印任何内容。

这是一个错误。一个旧的,那个。

参见SI-1980 and PR #2852

链接的拉取请求在使用 -Xlint 标志时添加了编译器警告:

<console>:13: warning: by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.
         def :: (x: =>Int) = new Node(x)  // a right-associative method
             ^

只要提到名字参数,就会对其进行评估。 The spec says 右关联运算符方法调用的计算方式如下:

a op_: b

脱糖为:

{ val someFreshName = a; b.op_:(someFreshName) }
//                   ↑↑↑
// Eval happens here ↑↑↑