Re: Will "$_[0]=shift" always resolve shift first?
by ikegami (Patriarch) on Sep 08, 2008 at 21:44 UTC
|
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.
| [reply] [d/l] [select] |
|
|
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). | [reply] [d/l] |
|
|
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!
| [reply] |
|
|
|
|
|
|
|
|
|
|
|
|
|
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".
| [reply] [d/l] [select] |
Re: Will "$_[0]=shift" always resolve shift first?
by moritz (Cardinal) on Sep 08, 2008 at 21:24 UTC
|
is this reliable?
Yes.
There's quit some code on CPAN that relies on the relative order of execution, und such things aren't changed without a very good reason. The ports want to use CPAN, not break it.
| [reply] |
|
|
Above, moritz says:
und such things aren't changed without a very good reason
Elsewhere in this thread, moritz offers the following aside:
(perltrap documents that the RHS is evaluated first, btw).
And let us see what perltrap actually says
LHS is evaluated first in perl4, second in perl5; this can affect the relationship between side-effects in sub-expressions.
So it appears there must have been a very good reason found when Perl5 was produced?
In any case, we now have documentation of the evaluation order of the sides of assignment expressions. This will cause many to feel more justified in relying on this order. Of course, the documentation is actually documentation of this order having already changed (with no motivation mentioned and I'd bet money on the motivation being "it was convenient to implement that way" because that is how little justification is needed for one to change the order of evaluation of sub-expressions and exactly why it is unwise to rely on such things). So I find this documentation to be justification for not relying on the "current" order of evaluation (and I suspect that part of the point of Perl 6 will mean that this order is likely to be less "constant" in Perl 6).
To be fair to moritz, the above node was posted before the one containing the aside (which also appears to backpedal significantly on the above simple, bold proclamation). But I felt the above simple, bold proclamation deserved a direct reply (including a link to the backpedaling).
Also, the claim that documented behavior of local($foo)= $foo; and my $bar= $bar; requires the "current" order of evalatution is quite bogus. Indeed, that behavior for local didn't change between Perl4 and Perl5 but the order of evaluation did, QED.
You can always evaluate the RHS and push the resulting value(s) onto the stack before you do anything with the LHS or after you've done everything to the LHS except for actually assigning the computed values over. The main determination as to what order you do those in is which way is more convenient for those results to appear on the stack.
Now, if Perl6 makes the mistake (IMHO) of allowing context (of the RHS of an assignment statement) to be ambiguous at compile time, then this would finally force the implementors' hands on this order of evaluation issue. It would also require changing away from the "current" Perl5 order of evaluation. More likely, being able to optimize (one of the core motivations for Perl6) almost always also means being able to change a lot of order of evaluation "decisions" pretty much willy-nilly. That is, some subtle change to code not even obviously connected to the assignment statement could change the order of evaluation. In Perl 6, there should even be the possibility of both sides being evaluated "at the same time" or interleaved.
| [reply] [d/l] [select] |
|
|
So it appears there must have been a very good reason found when Perl5 was produced?
I didn't think of major releases when I wrote that. If you sacrifice backwards compatibility anyway, (and I believe that the introduction of perl 5 did, but I'm too young to have been hacking at that time), any valid reason is good enough IMHO.
Also, the claim that documented behavior of local($foo)= $foo; and my $bar= $bar; requires the "current" order of evalatution is quite bogus. Indeed, that behavior for local didn't change between Perl4 and Perl5 but the order of evaluation did, QED.
Indeed, in these cases it's not really the order of evaluation that matters, but the scope. The scope of a my variable starts at the end of the statement in which it is declared, which you can easily test like this:
$ perl -Mstrict -e 'my $x = $x'
Global symbol "$x" requires explicit package name at -e line 1.
Now, if Perl6 makes the mistake (IMHO) of allowing context (of the RHS of an assignment statement) to be ambiguous at compile time
In Perl 6 it is known at parse time if an assignment is in list context of item context, if that is what you mean. And it has to be, because the list assignment infix:<=> has a different precedence than the item assignment infix:<=> operator, which allows for the magic of my @x = 1, 2, 3; and $x = 1, $y = 2 both to do what you mean. | [reply] [d/l] [select] |
Re: Will "$_[0]=shift" always resolve shift first? (meh)
by tye (Sage) on Sep 09, 2008 at 02:46 UTC
|
Some will argue that it is unlikely to change. Many invalid arguments will be given that claim to explain why the order has to be the way that it is. Nothing new there.
Do you really care? I don't.
I don't consider it reasonable to rely on the order of evaluation of expressions that aren't separated by a semicolon (or other statement boundary), a short-circuiting logical operator (&&, ||, etc.), or (sometimes) a comma. And the case of comma is very often one to avoid.
I don't want to waste time trying to convince myself that some dubious reliance on a particular order of evaluation is reasonable or not; it is usually much faster to just remove the dependence and make the code clearer (thus saving even more time in future).
| [reply] [d/l] [select] |
Re: Will "$_[0]=shift" always resolve shift first?
by betterworld (Curate) on Sep 08, 2008 at 23:41 UTC
|
@a = ('a', 'b', 'c');
{
local($a[1]) = 'foo';
local($a[2]) = $a[2];
is($a[1], 'foo');
is($a[2], 'c');
undef @a;
}
Now, I daresay (and I hope that my head is clearer than when I wrote my last node) that this only works if "=" evaluates its right-hand operand first.
The important line is the one with the second local. As a run-time operator, local() effectively sets its operand ($a[2] in this case) to an undefined value. But then this new value will be set to the value of $a[2] before local was executed. This would not work the other way round.
Apart from this extract, local.t contains more similar tests.
In a nutshell: Even though I could not find any hints in the documentation, the test suite suggests that kyle's coworker's code is reliable. | [reply] [d/l] [select] |
|
|
| [reply] |
Re: Will "$_[0]=shift" always resolve shift first?
by betterworld (Curate) on Sep 08, 2008 at 21:44 UTC
|
As you can see from the listing in perlop, the assignment operator ("=") has right associativity, which means that the right side is evaluated before the left side.
Incidentally, this is the very reason that the following common idiom works as you expect:
$x = $y = 42;
Had "=" its left side evaluated first, the 42 would not make it into the $y variable, which would lead to code breakage all over the planet ;)
Update: Hm, scratch that, I think I confused a few terms. The associativity tells us how the statement gets parsed, not how it gets executed. | [reply] [d/l] [select] |
|
|
Associativity controls the order in which operators are evaluated in a chain of operators of the same precedence.
If operand evaluation order was tied to operator precedence and associativity, bar() would be evaluated before foo() in
foo() + bar() * baz()
(It's not.) | [reply] [d/l] [select] |
|
|
use strict;
my @array = (0, 0, 0);
my $x = 0;
$array[++$x] = $x++;
print "@array\n";
$x = 0;
$array[++$x] = $x;
print "@array\n";
$x = 0;
$array[++$x] = ++$x;
print "@array\n";
<prints>
0 0 0
0 1 0
0 1 2
with Perl 5.8.7.
Perl reduces RSI - it saves typing
| [reply] [d/l] [select] |
|
|
What is your point?
If your point was to contradict betterworld, it doesn't. While it may not *appear* to be the case, the RHS of the assignment is evaluated before the LHS in every instance.
If your point is that it can get very complex very fast, I agree.
| [reply] |
|
|
|
|
|
Re: Will "$_[0]=shift" always resolve shift first?
by SuicideJunkie (Vicar) on Sep 09, 2008 at 17:30 UTC
|
I believe that the whole question about reliance on evaluation order is a distraction from the real issue...
Whether it is reliable or not will not change the fact that it is opaque:
To my dismay, this actually works. The effect is: ...
Note that your external explanation is 20x the size of the program code + comments. And how much time did it take you to determine that much?
That is just not right
Therefore, regardless of whether it works right now, it should be marked for rewriting and clarification. At the very least, comment your analysis above into the code so the next person to see that doesn't suffer the same.
PS: Compression ratio might be a good analysis to try: (Size of Code+Comments) vs (size of additional documentation needed to understand the code)
A higher ratio implies the code is clear, with a ratio of infinity being code that is completely self-explanatory.
| [reply] |