in reply to Will "$_[0]=shift" always resolve shift first?

Nowhere does Perl guarantee (or even document) the order in which operands are evaluated. For example, consider

var() = foo() + bar() * baz();

Perl is documented to multiply the result of bar() with the result of baz() before adding the product to the result of foo(), but it doesn't document when it calls the functions to get the results.

Can you guess the order?

I was going to say Perl always evaluates operands in left to right order, but it's not the case.

var() = foo() + bar() * baz(); 6 1 2 3 7 5 4

You can see that here:

my $x; sub var : lvalue { print 'var'; $x } sub foo { print 'foo'; 3 } sub bar { print 'bar'; 4 } sub baz { print 'baz'; 5 } local $\ = "\n"; var() = foo() + bar() * baz(); print($x);
>perl 709887.pl foo bar baz var 23

However, the order in which Perl evaluates operands is not platform- or compiler-specific. And some people rely on the order for some operators, particularly the comma operator. So it's not likely to change.

There's a catch where lvalues are involved. Even if you know the order in which operands are evaluated, you could be surprised by the results. The operands of the comma operator are evaluated left to right, but you'd think otherwise by looking at the following:

>perl -le"$i=3; print 0+$i,$i,++$i,$i" 3444

In short, you might be able to rely on the order in which operands are evaluated, but I consider it a bad practice. Avoid changing an argument and reading it in the same expression. At the very least, it reduces readability and maintainability. The length of this post is proof of that.

Replies are listed 'Best First'.
Re^2: Will "$_[0]=shift" always resolve shift first?
by moritz (Cardinal) on Sep 08, 2008 at 22:02 UTC
    I was going to say Perl always evaluates operands in left to right order, but it's not the case.

    It does, unless precedence or associativity gets in your way. Your example demonstrates this quite well:

    var() = foo() + bar() * baz(); 6 1 2 3 7 5 4 Precedence tightest: * + loosest: =

    So the arguments are evaluated left-to-right on the LHS of the =, then the sub expressions in order of their precedence, then the RHS. (perltrap documents that the RHS is evaluated first, btw).

    Update: the more I think about this, the less sure I am. So try not rely too much on it ;-)

    In short, you might be able to rely on the order in which operands are evaluated, but I consider it a bad practice

    Fully agreed.

    Second update

    Ok, the operands are evaluated left-to-right, the operators according to their precedence/associativity.

    The assignment operator is an exception (on which we rely quite often).

      It does, unless precedence or associativity gets in your way. Your example demonstrates this quite well:

      Not at all. My example shows that precedence has no effect on the operatoroperand evaluation order.

      And since associativity breaks ties in precedence, how could it possibly have any effect on the operatoroperand evaluation order if precedence doesn't.

      Updated (18:55 EDT): I keep typing "operator" when I try to type "operand"! argh!

        Sorry, but not entirely true. If you remove precedence, then things get evaluated in a different order:
        foo() + bar() + baz() 1 2 3 4 5
        The following script proves this:
        #! /usr/bin/perl use strict; package Foo; use overload '+' => sub { my ($self, $other) = @_; print "Evaluating $self + $other\n"; return Foo->new($$self + $$other); }, '0+' => sub {my $self = shift; $$self}, '""' => sub {my $self = shift; $$self}; sub new { my $class = shift; my $obj = \shift; print "Creating $$obj\n"; bless $obj, $class; } package main; Foo->new(1) + Foo->new(2) + Foo->new(4); __END__ Creating 1 Creating 2 Evaluating 1 + 2 Creating 3 Creating 4 Evaluating 3 + 4 Creating 7
        That said, it looks like Perl likes to evaluate binary operations by evaluating the left hand side, then the right hand side, then performing the operation. (As you found, = is an exception.) With that order, the fundamental operands will generally go left to right. The order of the intermediate operations can generally be predicted fairly easily from perlop.

        That said there are some points of precedence that are officially not documented. But that's because it is easier to say it is not documented than it is to explain why

        my $foo = 0; print ++$foo + $foo++;
        prints 3. (At least with Perl 5.8.)

        The reason is that you evaluate the pre-increment that turns $foo into 1 then return $foo. You copy the existing value into a temp, then increment $foo again. You then add $foo (now 2) to its old value (1) to get 3. While there is no need to change this, anyone who depends on it deserves the worst of what they might get at some future date.

        My example shows that precedence has no effect on the operator evaluation order.

        Not quite true. * is evaluated before +. (Which makes a difference for overloaded operators)

        And since associativity breaks ties in precedence, how could it possibly have any effect on the operator evaluation order if precedence doesn't.
        You're right here. After reading betterworld's comment I thought that the right associativity of the = operator was the key, but it isn't. It seems to be just a special case for =.
Re^2: Will "$_[0]=shift" always resolve shift first?
by DrHyde (Prior) on Sep 09, 2008 at 09:34 UTC

    Changing an argument and reading it in the same expression is Just Fine. Consider $x = $x + 1, or $number = next_in_sequence($number).

    Better advice would be "avoid stunt code".