6.4. Negative Control Statements
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: RANGE_CHECK: until ($measurement > $ACCEPTANCE_THRESHOLD) { $measurement = get_next_measurement( ); redo RANGE_CHECK unless defined $measurement; # etc. } 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:
while ($measurement <= $ACCEPTANCE_THRESHOLD) {
$measurement = get_next_measurement( );
redo RANGE_CHECK if !defined $measurement;
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: VALIDITY_CHECK: until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) { $measurement = get_next_measurement( ); redo VALIDITY_CHECK unless defined $measurement && $measurement ne '[null]'; # 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: while ($measurement < $ACCEPTANCE_THRESHOLD && $is_exception{$measurement}) { $measurement = get_next_measurement( ); redo VALIDITY_CHECK if !defined $measurement || $measurement eq '[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: until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) { The corresponding "positive" version should have been:
while ($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:
croak "$value is missing" if !$found; is not appreciably harder to comprehend than: croak "$value is missing" unless $found; unless or until are minor conveniences that can easily become major liabilities. They're never necessary, and frequently counterproductive. Don't use them.
|