Showing posts with label operator precedence. Show all posts
Showing posts with label operator precedence. Show all posts

Monday, September 21, 2009

hit by operator precedence and right associativity

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 ?