[Edinburgh-pm] Spot the bug

Aaron Crane perl at aaroncrane.co.uk
Mon Apr 29 03:10:21 PDT 2013


[I delayed posting this for a while, so as to avoid spoilering it for
anyone still playing along at home, so it has some overlap with Wim's
message from yesterday; sorry about that.]

Miles Gould <miles at assyrian.org.uk> wrote:
> return ($self->true_count($idx) > 0) xor ($self->false_count($idx) > 0);

> 1) Can you spot the bug in my code, and find a fix for it?
> 2) Can you explain why it makes sense for Perl to behave this way?

The "xor" operator (like "and" and "or") has lower precedence than
"return", so the routine returns only whether true_count > 0.  You can
get the desired result with careful parenthesisation, or in this case
(since the xdisjuncts are certain to be Perl's conventional truthy and
falsy values) with the bitwise-XOR operator:

return ($self->true_count($idx) > 0 xor $self->false_count($idx) > 0);
return $self->true_count($idx) > 0 ^ $self->false_count($idx) > 0;

Or in a variety of other ways:

return $self->true_count($idx) > 0 != $self->false_count($idx) > 0;
return !$self->true_count($idx) != !$self->false_count($idx);

use List::Util qw<sum>;
return 1 == sum map !$self->$_, qw<true_count false_count>;

I did detect the bug by inspection — you narrowed it down very
conveniently, after all — but B::Deparse is the go-to tool for
ensuring that precedence isn't getting in your way:

$ perl -MO=Deparse,-p -e 'sub f { return ($self->true_count($idx) > 0)
xor ($self->false_count($idx) > 0) }'
sub f {
    ((return ($self->true_count($idx) > 0)) xor ($self->false_count($idx) > 0));
}
-e syntax OK

The "-p" switch on the -MO=Deparse invocation is what enables the
"parenthesise wherever possible" behaviour.

This precedence lossage came up on p5p a couple of months ago, as it happens:

http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2013-02/msg00945.html

The consensus seems to be that this behaviour is hard to justify
(except as an accident of history), and Perl may at some point start
issuing a warning for (unreachable) comparands on the right-hand side
of a low-precedence logical operator whose left-hand side has
compulsory flow-control effects.  (Changing the precedence of "return"
seems more likely to have unforeseen consequences, and also doesn't
help with "die" or "next" or "last" or what-have-you.)

I believe your example is a useful contribution to the debate, in that
there's no high-precedence logical-XOR operator that could be
recommended in this situation.

-- 
Aaron Crane ** http://aaroncrane.co.uk/


More information about the Edinburgh-pm mailing list