I was making an operator precendence and associativity table for C when I obtained a weird result in my tests, for which Perl as the same weird behvior a priori.
The **
power operator is right associative, which means that expressions are evaluated right to left.
It obviously work with arithmetic exprssions correctly, but weirdly, it doesn't produce the expected result with the print function call.
I expected this to output 321
$ perl -E 'print("1") ** print("2") ** print("3"); say ""'
123
but it produce 123
This also happens with user-defined function:
$ perl -E 'sub my_print { print $_[0] } my_print(1) ** my_print(2) ** my_print(3); say ""'
123
Even more weird,
$ perl -E 'sub my_print { say $_[0]; $_[0] } say my_print(2) ** my_print(2) ** my_print(4);'
2
2
4
65536
when we defined a function with a print
side effect which alos return a value. The result of the arithmetic expression has the correct expected value, with the evaluation in the order defined by the operator associativity, but the print
still defies operator associativity.
It's as if there were 2 order of evaluations.
- The normal order of evaluation, which is defined by the AST and the algorithm wakling over it to evaluate it (or more likely during the "evaluation-order pass" explained in
man perlguts
andop.c
). - A second evaluation order for the side-effets or I/O where it seems that function calls of builtin that have a side effects are put in a queue, which brings the question: In which order are they placed
I am going to assume that it is not a bug, but a feature. If it were a bug it would have been discovered long time ago.
So this bring my actual question : What is the rational for this behavior ? and How is it implemented ?. (meaning which data structures are used and during which compile time phase does this happens).
If I'm right thinking that there are an AST level queue somewhere, in which order are the print
calls enqueued ?
EDIT1: As an additional precision, I know that queues are involved during I/O, but it doesn't change the fact that I/O operations are normally enqueued in the evaluation order of the program, not in an arbitrary left-to-right order of the program text.
CodePudding user response:
You're confusing operand evaluation order and operator precedence/associativity.
Operator associativity dictates if
X ** Y ** Z // 2 ** 2 ** 3, for example
means
( X ** Y ) ** Z // ( 2 ** 2 ) ** 3 = 64
or
X ** ( Y ** Z ) // 2 ** ( 2 ** 3 ) = 256
As you mentioned, **
is right-associative, so it means the latter.
It has nothing do with the order in which X
, Y
, Z
and Y ** Z
are evaluated relative to each other. As long as Y
and Z
are evaluated before Y ** Z
, the above produces the same result no matter the order in which those four expressions are calculated.
Some conclusions can be drawn. As mentioned, Y
and Z
obviously need be evaluated before Y ** Z
. But absent such requirements[1], and absent explicit statements to the contrary in the documentation[2], the operators can be evaluated in an order of the compiler's choosing.
Another example is that short-circuiting operators necessarily need to evaluate their left-hand side operand before their right-hand side operand.
The comma operator is guaranteed to evaluate each operand in order.