2

I have this line of code in a toy-language. The print-function takes a list of arguments and prints those.

print(a, (a := 5, a))

Would there be a difference in the output if I used call-by-value or call-by-name? If so, what would the outputs be.

It can be asumed that a is initialized to 0.

Borimino
  • 23
  • 2
  • 2
    Depends on the implementation of `print` – Bergi Sep 09 '16 at 12:51
  • What is your stance at it? What material on evaluation strategies have you read, what is still unclear? – Bergi Sep 09 '16 at 12:51
  • I am writing a compiler for a project, and someone told me that in call-by-name it would print `0 5` whereas with call-by-value it would print `5 5`. His argument was, that under call-by-value it would first evaluate all the expressions, and afterwards print the results. Is there any truth to this? – Borimino Sep 09 '16 at 12:55
  • If at all, he seems to have gotten it reverse. A call-by-value with strict evaluation and left-to-right evaluation of arguments would pass `0` and `5`. – Bergi Sep 09 '16 at 12:59
  • I thought "call-by-name" meant passing arguments by reference, but it actually means lazy evaluation of arguments, where the argument is evaluated once for each 'usage' in the function body. What useless terminology. – Cauterite Sep 09 '16 at 13:03

1 Answers1

4

With "call-by-value" arguments are usually evaluated left-to-right (in most languages), so the expression would be equivalent to something like this:

arg1 := a // copy value of a to arg1
a := 5 // copy 5 to a
arg2 := a // copy value of a to arg2
print(arg1, arg2) // print(0, 5)

"call-by-name" is apparently a form of lazy evaluation which would yield something like this:

arg1 := function() {return a;}
arg2 := function() {a := 5; return a;}
print(arg1, arg2)

So in this case the result will depend on two things:

  • whether, in this language, closures capture variables by reference or by value. If capture by value, the a := 5 won't affect the value of a that the first closure has captured. However, most languages which allow reassigning of local variables implement capture-by-reference (e.g. JavaScript).
  • The order in which the print function decides to evaluate its arguments - depends how it's written.

If the closures capture by value, the print(…) will yield 0 5, because the assignment a := 5 only affects the second closure's copy of a.

If closures capture by reference then I can only guess at what the output might be. But it's quite likely the print function will do something like this:

print := function(lazy x, lazy y) {
    writeToOutput(x())
    writeToOutput(y())
}

In which case the result will be the same (0 5), because x() is evaluated first, the result processed, then y() is evaluated. In this case the value of a doesn't change until after the function is done with x.

But this is only a guess; print could evaluate them in any order (and any number of times).

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Cauterite
  • 1,637
  • 17
  • 24
  • "arguments are usually evaluated left-to-right (in most languages)" Notable counter examples are C, C++ and OCaml. In those languages evaluation order of function arguments is unspecified (and often implemented as right-to-left). – sepp2k Sep 09 '16 at 16:04
  • 1
    @sepp2k: also scheme and friends – rici Sep 09 '16 at 17:11