• Ian Stuart's picture

    The joys of precedence (in Perl)

    Ian Stuart / March 31, 2015
  • 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 FASLE 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