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
unless
with 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
,unless
implies 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
if
and 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
unless
to anif
to understand it; in these cases, usingif
is better.Of course, even if you use
unless
because 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
elsif
orelse
block.unless ($foo) { ... } else { ... }
Yep.