问题 如果方法是中缀和右关联,为什么Scala会为name-by-name参数计算参数?


据我所知 call-by-name 方法的参数,相应的参数表达式在传递给方法时不会被计算,但只有在方法体中使用参数的值时(和if)。

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

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

(我使用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??")

9521
2017-11-11 17:42


起源



答案:


只要提到它们,就会对名称参数进行评估。 规范说 右关联运算符方法调用的计算方式如下:

a op_: b

desugars:

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

3
2017-11-11 22:52





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

看到 SI-1980年 和PR #2852

链接的pull请求在使用时添加了编译器警告 -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
             ^

10
2017-11-11 17:52



谢谢,m-z,快速而正确的答案。显然,它不会让我开心,但这不是你的错;-) - Holger Peine
我不会称那是一个错误。据我所知,这就是规范所说的,以及规范总是说的,只要右关联中缀操作符调用已经在语言中。通常人们抱怨当编译器不遵循规范时,现在确实如此,并且他们仍然不满意:-D(我理解并且完全同意指定的行为使得名称参数对于右关联中缀操作符调用完全无用,但是当规范不同意实施时,一个错误是,当你和我不同意规范时! - Jörg W Mittag
@JörgWMittag足够公平。 SI-1980中的讨论指出6.12.3与规范中的4.6.2有些矛盾。如果它不被认为是一个错误,那么我的问题仍然是开放的并且标记为错误。 - Michael Zajac
@ m-z:基本上是一种无法解决的紧张关系,试图让评估主要是从左到右和懒惰的评估。在这种特殊情况下,你无法双管齐下。你,OP和我会做出不同的选择,但它仍然是一个有效的选择。 - Jörg W Mittag


答案:


只要提到它们,就会对名称参数进行评估。 规范说 右关联运算符方法调用的计算方式如下:

a op_: b

desugars:

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

3
2017-11-11 22:52





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

看到 SI-1980年 和PR #2852

链接的pull请求在使用时添加了编译器警告 -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
             ^

10
2017-11-11 17:52



谢谢,m-z,快速而正确的答案。显然,它不会让我开心,但这不是你的错;-) - Holger Peine
我不会称那是一个错误。据我所知,这就是规范所说的,以及规范总是说的,只要右关联中缀操作符调用已经在语言中。通常人们抱怨当编译器不遵循规范时,现在确实如此,并且他们仍然不满意:-D(我理解并且完全同意指定的行为使得名称参数对于右关联中缀操作符调用完全无用,但是当规范不同意实施时,一个错误是,当你和我不同意规范时! - Jörg W Mittag
@JörgWMittag足够公平。 SI-1980中的讨论指出6.12.3与规范中的4.6.2有些矛盾。如果它不被认为是一个错误,那么我的问题仍然是开放的并且标记为错误。 - Michael Zajac
@ m-z:基本上是一种无法解决的紧张关系,试图让评估主要是从左到右和懒惰的评估。在这种特殊情况下,你无法双管齐下。你,OP和我会做出不同的选择,但它仍然是一个有效的选择。 - Jörg W Mittag