8

Here is the following problem. When I run the following:

object  Test {
  def /:(s: => Unit) = {
    println("/:")
    s
  }
}

println("A") /: Test

It prints:

A
/:

However, I was expecting it to print:

/:
A

since the last expression was supposedly rewritten Test./:(println("A")) - which by the way, gives the second value.

Does anybody know a way to make the first syntax work, e.g. println("A") /: Test but with call-by-name ?

Edit

Using the desugar method, I found out that the calls are desugared differently.

> desugar { println("A") /: Test}
 val x$1: Unit = println("A");
 Test./:(x$1)

Hence I am still wondering why this choice.

Community
  • 1
  • 1
Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101
  • 1
    Why do you "suppose" that the expression is equivalent to `Test./:(println("A"))`? The specification says: "A left-associative binary operation `e1 op e2` is interpreted as `e1.op(e2)`. If `op` is right-associative, the same operation is interpreted as `{ val x = e1; e2.op(x) }`, where `x` is a fresh name." So, it is equivalent to `{ val x = println("A"); Test./:(x) }`. – Jörg W Mittag Apr 07 '17 at 01:05

2 Answers2

10

It is a known issue. If you compile with -Xlint option you should see a warning.

$ scalac -Xlint temp.scala
temp.scala:2: warning: by-name parameters will be evaluated eagerly when called 
              as a right-associative infix operator. For more details, see SI-1980.
    def /:(s: => Unit) = {
        ^
one warning found
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
0

The problem, I think is the fact that you expect a by name parameter of type Unit - there is only one unit, hence the name: ().

I found the following works:

scala> :paste
// Entering paste mode (ctrl-D to finish)

object  Test {
  def /:(s: () => Unit) = {
    println("/:")
    s()
  }
}

val p = () => println("A")
p /: Test

// Exiting paste mode, now interpreting.

/:
A
defined object Test
p: () => Unit = <function0>
res5: Any = ()
Yaneeve
  • 4,751
  • 10
  • 49
  • 87
  • I bet this work, but the problem is not because of the Unit. I want to be able to write to the end of the line `:/ Test` and have something be printed before. I don't want to rewrite the argument itself. – Mikaël Mayer Apr 06 '17 at 17:36