Почему Scala оценивает аргумент для параметра по умолчанию, если метод является инфиксным и право-ассоциативным?
Как я понял параметры 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??")
Ответы
Ответ 1
Аргументы по-имени оцениваются всякий раз, когда они упоминаются. Спектр говорит, что вызовы метода право-ассоциативного оператора оцениваются следующим образом:
a op_: b
desugars:
{ val someFreshName = a; b.op_:(someFreshName) }
// ↑↑↑
// Eval happens here ↑↑↑
Ответ 2
Это ошибка. Старый, при этом.
См. SI-1980 и 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
^