Last Updated: February 25, 2016
·
178
· perllaghu

The joys of precidence

Precidence in Perl can catch you out.... it caught me out!

I have a script which emails some results to a subscriber on a daily basis. If there are no results, then it doesn't email the user - you only get an email if there's something to see, in other words.
As a reminder, the script will email a subscription-reminder to the user on the 1st of the month..... but only if they haven't been sent a results email that day.

The code went something like this:

my $has_emailed = email($recipient, $data);
my @t = localtime;
if ( not $has_emailed && not $t[3] ) {
   my $text = get_reminder_message();
   email( $recipient, $text);
}

The problem was that this was emailing a reminder every day!

Why? Precidence.

The && is a "short-circuiting and", and has a higher precedence that the not. This means that the statement actually reads as:

if ( not ($has_emailed && not $t[3]) )

so following the logic, we get:

  • $t[3] is the day-of-the-month, starting at 0 - FALSE for the 1st, and TRUE for every other day
  • not $t[3] is therefore TRUE on the 1st, and FALSE every other day
  • $has_emailed is TRUE if results were went to a user, FALSE if not.... we're assuming FALSE here
  • ($has_emailed && not $t[3]) is therefore FALSE
  • not ($has_emailed && not $t[3]) becomes true.

There are a number of solutions:

if ( (not $has_emailed) && (not $t[3]) ) {...} # Be explicit where the not's go
if ( !$has_emailed && !$t[3] )           {...} # ! has a higher precedence that &&
if ( $has_emailed || $t[3] ) {}   else   {...} # cast the problem into the negative
unless ( $has_emailed || not$t[3] ))     {...} # Perl::Critic doesn't like unless