The conditional operator only evaluates what's necessary, but you have a precedence problem.
First of all, the conditional operator is indeed guaranteed to use short-circuit evaluation, meaning it only evaluates what's necessary.
$ perl -M5.010 -e' sub f { say "f" } sub g { say "g" } $ARGV[0] ? f() : g(); ' 0 f $ perl -M5.010 -e' sub f { say "f" } sub g { say "g" } say $ARGV[0] ? f() : g(); ' 1 g
This is even true for E1 || E2, E1 or E2, E1 && E2 and E1 and E2. They only evaluate their right-hand side operand if necessary.
$ perl -M5.010 -e' sub f { say "f"; $ARGV[0] } sub g { say "g"; $ARGV[1] } say f() || g(); ' 3 4 f 3 $ perl -M5.010 -e' sub f { say "f"; $ARGV[0] } sub g { say "g"; $ARGV[1] } say f() || g(); ' 0 4 f g 4
This is why you can can evaluate open(...) or die(...) safely. Without short-circuiting, it would evaluate die whether open was successful or not.
Now, let's explain the following:
$s = "X"; 1 ? $s .= "_" : $s = "_"; say $s; # _
It's a precedence problem. The above is equivalent to
$s = "X"; ( 1 ? ($s .= "_") : $s ) = "_"; say $s; # _
$s .= "_" returns $s, so the conditional operator returns $s, so the string _ is assigned to $s. If we add parens to get the desired parsing, we get the expected result.
$s = "X"; 1 ? ($s .= "_") : ($s = "_"); say $s; # X_
Alternative:
$s = "X"; $s = ( 1 ? $s : "" ) . "_"; say $s; # X_
int a = 87; 1 ? a += 1 : a = 1;will setato 88, not to 1. In plain C it doesn't matter, as that combination is always an error. Unfortunately, it's common for "C-like" languages to get the?:operator wrong, perl and php being too egregious examples.