Showing posts with label syntax. Show all posts
Showing posts with label syntax. 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 ?