6.4. Negative Control Statements
Don't use unless or until at all .
Perl is unusual amongst programming languages in that it provides not only positive conditional tests (if and while), but also their negative counterparts (unless and until). Some people find that these keywords can make certain control structures read more naturally to them:However, for many other developers, the relative unfamiliarity of these negated tests actually makes the resulting code harder to read than the equivalent "positive" version:
RANGE_CHECK:
until ($measurement > $ACCEPTANCE_THRESHOLD) {
$measurement = get_next_measurement( );
redo RANGE_CHECK unless defined $measurement;
# etc.
}
More importantly, the negative tests don't scale well. They almost always become much harder to comprehend as soon as their condition has two or more components, especially if any of those components is itself expressed negatively. For example, most people have significantly more difficulty understanding the double negatives in:
RANGE_CHECK:
while ($measurement <= $ACCEPTANCE_THRESHOLD) {
$measurement = get_next_measurement( );
redo RANGE_CHECK if !defined $measurement;# etc.
}
So unless and until are inherently harder to maintain. In particular, whenever a negative control statement is extended to include a negative operator, it will have to be re-cast as a positive control, which requires you to change both the keyword and the conditional:
VALIDITY_CHECK:
until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) {
$measurement = get_next_measurement( );
redo VALIDITY_CHECK unless defined $measurement && $measurement ne '[null]';
# etc.
}
Not only is that extra effort, it's error-prone effort. Reworking conditionals in that way is an excellent opportunity to introduce new and subtle bugs into your program...just as the previous example did. The original until condition was:
VALIDITY_CHECK:
while ($measurement < $ACCEPTANCE_THRESHOLD && $is_exception{$measurement}) {
$measurement = get_next_measurement( );
redo VALIDITY_CHECK if !defined $measurement || $measurement eq '[null]';
# etc.
}
The corresponding "positive" version should have been:
until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) {
This kind of mistake is unfortunately common, but very easy to avoid. If you never use an unless or until, then you'll never have to rewrite it as an if or while. Moreover, if your control statements are always "positive", your code will generally be more comprehensible to all readers[*], regardless of the complexity of its logical expressions. Even in the simple cases, something like:
while ($measurement <= $ACCEPTANCE_THRESHOLD || $is_exception{$measurement}) {
[*] Including yourself in six months' time . . .
is not appreciably harder to comprehend than:
croak "$value is missing" if !$found;
unless or until are minor conveniences that can easily become major liabilities. They're never necessary, and frequently counterproductive. Don't use them.
croak "$value is missing" unless $found;
Don't Fail Not to Use Chained NegativesThe "Negative Control Statements" guideline has proved to be one of the most controversial in this book. Though few people will miss until, many very good Perl programmers feel that unless is a valid choice, which improves readability in a limited number of circumstances.If you, or your team, are persuaded by such arguments and decide to allow the use of unless as part of your coding practices, then make sure that you only ever use it in ways that genuinely improve the readability of your code. The best way to ensure that is to require that the conditional expression of any negative control statement must always itself be expressed using "positive" logic.That is, never write an unless (or an until) in which the condition includes a "negative" operator, an inequality, or any kind of ordering test. Don't use any of the following operators with an unless or an until: Abiding by that rule would permit the following limited uses: but would still prohibit confusing double-negatives and reversed inequalities like: Remember too that, if you do choose to allow unless or until, exTRa attention will be required whenever a conditional expression is extended. In particular, if the extension adds a "negative" of any kind, then you will need to convert the negative control statement to the appropriate positive form, and rewrite of the original condition accordingly.The added degree of complexity and awareness that this transformation always requires and the risk of introducing errors that it inevitably involves are two good reasons to reread the "Negative Control Statements" guideline, and to reconsider banning unless and until altogether. |