How I use unless
9th January 2013 · 2 min read
unless is a language construct that I think sums up Perl in many ways. In the right
hands, such syntactic choices can bring the code closer to the problem domain, aid
readability, and allow great freedom of expression. In the wrong hands, it can produce
a horrible mess which nobody can understand.
I have some simple heuristics for applying unless that have worked well for me and people
who read my code:
-
Use unless when the condition is negative.
You can avoid a double-negative by using
unlesswith negatively-named variables or functions. For example:lock() unless $skip_locking;…compared with
lock() if !$skip_locking; -
Use unless to imply success.
Unlike the neutral
if,unlessimplies that the block is likely to be executed (i.e. the condition is unlikely to occur, just as it does in English). It also implies that we want the block to execute.unless ($going_to_throw_up) { drink_shot(); # implication: throwing up is unlikely. Cheers! }vs.
if ( ! $going_to_throw_up ) { drink_shot(); # implication: uhm, I'm feeling a little woozy... }Even the notorious double negative can work in this context.
unless ( ! file_readable($fh) ) { # do some shiz with $fh }Contrast with the more neutral
if ( file_readable($fh) ). -
Don’t use unless for complex expressions.
If the programmer needs to apply DeMorgan’s law to understand the conditional, use
ifand apply it immediately. For instance, which of these two are more readable?unless ( $have_work && !$sunday ) { sleep_in(); } if ( !$have_work || $sunday ) { sleep_in(); }Most likely, you had to mentally transform the
unlessto anifto understand it; in these cases, usingifis better.Of course, even if you use
unlessbecause a conditional is simple, it could get more complicated later. Refactoring the conditional into its own function or method protects you from this.unless ( $query->param('queue') or $query->param('language') or $query->param('domain') ) { # Provide default status filter } unless ($query->has_status_filter) { # Provide default status filter } -
Don’t use unless if you have an
elsiforelseblock.unless ($foo) { ... } else { ... }Yep.