Почему Scala оценивает аргумент для параметра вызова по имени, если метод является инфиксным и правоассоциативным?

Как я понял 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 = ... больше ничего не печатает.


person Holger Peine    schedule 11.11.2015    source источник


Ответы (2)


Аргументы по имени оцениваются всякий раз, когда они упоминаются. В спецификации сказано, что правоассоциативный Вызовы методов оператора оцениваются следующим образом:

a op_: b

десахариды для:

{ val someFreshName = a; b.op_:(someFreshName) }
//                   ↑↑↑
// Eval happens here ↑↑↑
person Jörg W Mittag    schedule 11.11.2015

Это ошибка. При чем старый.

См. 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
             ^
person Michael Zajac    schedule 11.11.2015
comment
Спасибо, m-z, за быстрый и правильный ответ. Конечно, меня это не радует, но это не твоя вина ;-) - person Holger Peine; 11.11.2015
comment
Я бы не назвал это багом. Это то, что говорит спецификация, и то, что спецификация всегда говорила до тех пор, пока правоассоциативные инфиксные операторы были в языке, насколько мне известно. Обычно люди жалуются, что компилятор не следует спецификации, теперь это так, и они все еще недовольны :-D (я понимаю и полностью согласен с тем, что указанное поведение делает аргументы по имени совершенно бесполезными для правоассоциативных вызовов инфиксных операторов , но баг - это когда спецификация расходится с реализацией, а не когда мы с вами не согласны со спецификацией!) - person Jörg W Mittag; 12.11.2015
comment
@JörgWMittag Достаточно честно. Обсуждение в SI-1980 указывает на то, что 6.12.3 несколько противоречит 4.6.2 в спецификации. Если это не считается ошибкой, то я не понимаю, почему проблема все еще открыта и помечена как таковая. - person Michael Zajac; 12.11.2015
comment
@m-z: По сути, это неразрешимое противоречие между попыткой сделать оценку в основном слева направо и ленивой оценкой. В данном конкретном случае у вас не может быть и того, и другого. Вы, ОП и я сделали бы другой выбор, но, тем не менее, это правильный выбор. - person Jörg W Mittag; 12.11.2015