in reply to Re^9: Setting signal handlers considered unsafe?
in thread Setting signal handlers considered unsafe?

It was a small surprise to me, therefore, that local $fred = $fred ? "Still $fred" : 'undef' ; sets $fred to 'Still 78'
Although it's not documented, all Perl operators except assignment operators evaluate their operands in left-to-right order. Assignment operators evaluate their RHS before their LHS.

I'm no fan of code that depends on the order of evaluation of terms -- side effects should be handled carefully, and an expression with multiple, interdependent side effects doesn't look careful to me. (With the exception of '&&' et al, where order is an essential element.) Not forgetting one's primal fear of some hairy-arsed optimiser barging in and laying waste the carefully arranged crockery. So the fact that it's not documented is not an issue, though a formal "not-specified" wouldn't hurt.

That said, left to right evaluation of the terms is not going to surprise. I note that subroutine arguments are also evaluated left to right -- which isn't always the case in other languages.

While I was poking around trying to get a handle on this I thought perhaps the rightward association might be significant. The only other right associative binary I could find is '**'. Experiment shows that this evaluates its arguments left to right too.

The exception for '=' is interesting. Obviously, the LHS of an assignment needs to be evaluated to an lvalue -- the address (in some form) of the destination. Equally obviously, unless one side has a side-effect that affects the other, the order of execution is immaterial. So, making an exception for '=' must have something important in mind. I'm curious what it was. The simple:

$x = $x + 1 ;
could happily take the address of $x then evaluate $x + 1 and then assign... surely ? So, what could one reasonably expect:
$x = 0 ; $a[$x++] = $x ; # $a[0] = 0 or $a[0] = 1 ?? $a[$x] = $x++ ; # $a[2] = 1 or $a[1] = 1 ??
to do ? (Apart from making one queasy.) Is resolving this a good reason for making '=' an exception ? More contrived, but essentially the same:
sub h { $_[0] = 1 ; return '!' ; } ; $x = 0 ; $a[$x] = h($x) ; # $a[0] = '!' or $a[1] = '!' ??
However simple or complicated one makes this, it comes down to the same thing... is it somehow "obvious" that because we think of the RHS being assigned to the LHS, that the order of execution should favour that ? And, is that worth bending the also "obvious" rule that terms are evaluated left to right ?

Your friend and mine C99 is happy specifying that for an assignment operator, the order of evaluation of its operands is unspecified. Given that Perl doesn't specify the order, and "Good" code would not depend on it anyway -- if only for the sake of clarity -- I guess this is all has, at most, curiosity value.

My (completely unsubstantiated) guess is, however, that the original motivation for making an exception for '=' is our friend local -- which is where we came in. It is "obvious" that:

$x = $x + 1 ; # or $x = $x++ ;
and:
local $x = $x + 1 ; # or local $x = $x++ ; (!)
should yield the same value for $x. But, given that local is just an operator (same like everything else), straightforward left to right evaluation does not do the trick. To fix that one could fiddle with local and/or the parsing thereof. Or fiddle with '=', with the side effect of choosing the less unobvious ordering in other cases. (And, since the order is not defined, who cares ?!) If this were the case, then things have now come full circle :-)

Anyway, putting away this idle speculation (however amusing it might be)... While I was poking at this, I came across the following:

sub d { $_[0] = 10 ; return 1 ; } ; $x = 78 ; print "\$x=$x. \$x + d(\$x) = ", $x + d($x), " \$x=$x\n" ; $x = 78 ; print "\$x=$x. d(\$x) + \$x = ", d($x) + $x, " \$x=$x\n" ;
which gives:
  $x=78.  $x + d($x) = 11  $x=10
  $x=78.  d($x) + $x = 11  $x=10
After some headscratching, and having removed the splinters from under my nails, I decided to discount quantum effects... I assume this is an effect of an optimisation which avoids taking a copy of the value of $x. That's fine. The order of execution is not defined, so why should one expect one outcome and not another :-) ?

Using your little trick to observe the order of evaluation:

sub d { $_[0] = 10 ; return 1 ; } ; sub o { print $_[1] ; return $_[0] ; } ; $x = 78 ; print "\$x=$x. \$x + d(\$x): " ; print o($x, 'LHS ') + o(d($x), 'RHS '), " \$x=$x\n" ; $x = 78 ; print "\$x=$x. d(\$x) + \$x: " ; print o(d($x), 'LHS ') + o($x, 'RHS '), " \$x=$x\n" ;
I get:
  $x=78.  $x + d($x): LHS RHS 79  $x=10
  $x=78.  d($x) + $x: LHS RHS 11  $x=10
which could, of course, be the effect of introducing an observer into the system... but more likely due to the actual copying of the value of $x, which the simpler expression is optimising away.

Well, there you go. No matter how tightly one tries to bind one's abstract model to the implementation, there's always something !

Replies are listed 'Best First'.
Re^11: Setting signal handlers considered unsafe?
by ikegami (Patriarch) on Nov 11, 2008 at 19:42 UTC

    In short, "don't do that". But if you're interested in the internal details, read on.

    Values in Perl are stored in a struct call "SV" (or a derivative thereof). SVs aren't copied around. Pointers to SVs are. That results in "pass by reference" being the default model for every operator, function and sub. This creates interesting side-effects as seen in the following:

    >perl -le"$x=3; print($x+0, $x, ++$x, $x, $x++, $x, $x+0);" 3555455

    $x and ++$x place a pointer to $x's SV on the stack. $x+0 (and "$x" for strings) creates a new SV, so it's no affected by later changes to $x. Similarly, $x++ creates a new SV containing a copy of what $x was at the time of the increment.

      Of course, you should never do that in C++. The standard specifically states that modifying a variable more than once in the same statement results in either undefined or implementation defined behavior (depending on what is actually being done). In you case, it's implementation defined, because there is no way to know the order of evaluation of the arguments. You also don't know when the post-increment happens, except that it will occur before the subroutine actually starts.

      And technically, 0+x and ++x return r-values, not constants.

      Sorry about that. I'm a recovering C++ programmer.<grin/>

      G. Wade

        The point was to show what Perl does. I specifically mentioned the result was specific to my compiler.

        The standard specifically states that modifying a variable more than once in the same statement results in either undefined or implementation defined behavior (depending on what is actually being done).

        I believe foo(x++, x) is also undefined even though it only modifies the variable once.

        Basically, the same thing applies to Perl. While the order is predictable, relying on it is dangerous. I've seen the following a fair bit, though, which modifies and uses @_:

        sub foo { shift->bar(@_) }