While studying a bug, I wrote the following test program :
use strict;
use warnings;
use Data::Dumper;
my $bool = 1;
my %h;
$bool ? $h{true} = 't' : $h{false} = 'f';
print Dumper(\%h);
The ternary expression starting with $bool was supposed to be a concise way to write a conditional, but the result was a disaster. Can you guess the output ?
Here it is : $VAR1 = { 'true' => 'f' };
This really seems totally insane : something is assigned to the 'true' slot of the hash, but the value comes from what was supposed to be in the 'false' slot !
OK, the ternary expression above is wrong, because the '?:' operator has higher precedence than '=', so one should really write
$bool ? ($h{true} = 't') : ($h{false} = 'f');
But how comes that perl issues no error, no warning, and happily produces a very strange result ? It seems that both sides of the conditional are executed simultaneously, and collapse in a mysterious way.
I tried running the script through B::Deparse to understand how it was parsed, but the output was exactly like the original source, so it really seems to be legal Perl !
It really took me a while until the 'aha' moment that made me realize that because of right-associativity, and because conditional expressions can be lvalues, and because "Unlike in C, the scalar assignment operator produces a valid lvalue" (perlop dixit), this was parsed as
($bool ? ($h{true} = 't') : $h{false}) = 'f';
So the $bool test chooses an lvalue between $h{true} and $h{false}, and it doesn't matter that this lvalue is first assigned a 't', because later the main assignment puts a 'f' into it.
Obvious, isn't it ?
Showing posts with label operator precedence. Show all posts
Showing posts with label operator precedence. Show all posts
Monday, September 21, 2009
Subscribe to:
Posts (Atom)
