Think Positive

When you write code, create definitions, and name variables, it is best to think positive. Avoid temptations to think negative.

Imagine we are creating some software to control an external device. Maybe an embedded system with some tightly coupled hardware. We might be working on a joint of a robot arm.  A change is needed in the hardware and suddenly you have no hardware but you must continue your work.  Or maybe the hardware hasn't been developed yet and you need to use an emulator. 

Let's call our device the Rotator. For some reason we need to run and test our code when the rotator is not really installed. We think about it and create a define:

  #define NO_ROTATOR
Or in the IDE or pre-processor we declare NO_ROTATOR Now in our code we can write
  #ifdef NO_ROTATOR
    <code for when it is not there>
  #else
    <code for when it is there>
  #endif
And all is well. Until somewhere else we need a few lines of code for specifically when the rotator is there. Given that we already have the NO_ROTATOR we would probably write:
    #ifndef NO_ROTATOR
       <code>
    #endif

What have we just written? Speaking this out loud we say: “If it is not true that we do not have a rotator, then < do this.>”
When you say it out loud, you can more easily detect the awkwardness.
Sooner or later we will need a #else which will really mean:
“Else if is true that we do not have a rotator, so <do that>”

We may have several types of rotators, maybe one that does not have the touch sensor installed. Then we may have a construct like this:

  #ifndef NO_ROTATOR
    <code>
     #ifndef NO_TIP_SENSOR
        <code>
     #else
        <code>  // pop quiz, quick, what two conditions are in effect here?
     #endif
  #else
     <code>
 #endif
>/p>

Did you try the pop quiz? Did you quickly get the sense correct? Remember, there are two double negatives and the quiz was within the else of one if clause and before the else of the other if clause. When you must stop and think about it, the chances of making an error are greater than when you can read it and understand without conscious thought.

When you start adding error control logic, function calls, and maybe more macros, sooner or later the double negative will catch you and trip you up. So why take the chance?

A large part of the science and art of writing good code is to write it so that your successor will be able to easily understand your intent.  The easier you make it, the less likely they will err when they modify your code.

Even more important, you are less likely to trip yourself up. We all do that on occasion.

Every time you write a define, a macro, or even create a variable or a procedure name, think of the best way to express the condition in a positive sense. Remove all the negatives. Then create your expression in the positive sense.

In this example we might use

   #define ROTATOR_ATTACHED
Then we could write:
   #ifndef ROTATOR_ATTACHED
       <code>
   #endif.
You probably caught that one. I used a negative.
   #ifdef ROTATOR_ATTACHED
       // place holder
   #else
      <code>
 #endif

Odds are, sooner or later, you will need to remove the place holder comment and add some code. When you do, you don't need to change the structure at all. In the meantime, it helps the next engineer recognize what you were doing. It says straight up that you really did not want to add anything when the rotator is attached and you wanted to add code when it is not attached.

There are many that will say the first two lines of the above fragment are dead code, do nothing, and must be removed. First, I am pretty sure that the compiler will do that for you. It won't make it into the executable. Second, as already noted, plan for the future and make the next change easier. Third, we have eliminated the negative with a cost of only three of four seconds to type it in, versus the ease of straight forward reading.

Shortly after writing and posting the first version of this page, I wrote a page on loop control found here.     To make my point there, I selected a passage from the first edition of McConnel's book Code Complete. In Chapter 15, Controlling Loops, he uses this example:

    01 FOREVER
    02 {
    03    GetNextRating( &RatingIncrement );
    04    Rating = Rating + RatingIncrement;
    05    If( ! (Score < TargetScore && RatingIncrement !=0) )
    06        break;
    07
    08    GetNextScore( &ScoreIncrement );
    09    Score = Score + ScoreIncrement;
    10 }
Examine line 05 and 06, the two that define when we escape the loop. It is not immediately apparent when the loop exits. Lets read it out loud:
    When it is not the case that
       (
       Score is less than TargetScore
       AND
       RatingIncrement !=0
       )
We must mentally invert the less than and un-negate the not equal to. Even then, do we really understand what the original writer intended? Possibly not.

I have a better idea, rewrite the logic. I thought about it for a bit and derived the correct logic. While writing it up I remembered Mr. De Morgan, and I ran a quick qoogle search for wiki and demorgan which returned this page. (Note, I like wikipedia but have a bit of dyslexia and never seem to spell it right so I threw those two phrases at google and thanks to their rather kind interpretation of my request, found the right page immediately.) Right at the top of the page we find this:

not (P and Q) = (not P) or (not Q)
not (P or Q) = (not P) and (not Q)

We have a direct application of the first rule. Distribute the outside not function (The ! character) amongst the two inner comparisons while inverting the conjunctive logical to get:

( not( Score < TargetScore ) ) OR ( not (RatingIncrement !=0) )

Then separately working each of the two double based comparisons we arrive at:

Score > TargetScore OR RatingIncrement == 0

Now we can write the escape clause legibly as:

    if(  Score > TargetScore ||  RatingIncrement == 0 )
      break;

Lets read it again:

    If the score is greater than our target score
      OR
    if the rating increment is zero, we exit.

Whew! That is much easier to understand. We can be certain we and our successors understand the intent.

There are many situations in which we do need to deal with the negative sense of something. It those cases, it is far better to use the negative sense than to contrive a method of using the positive sense. I suspect that it would be rather difficult to draft a rule that specifies when we should or should not use a negative sense. (If you have one, please send it.)

But I can say this. Imagine that you are presenting your code in a peer review of your harshest, but fair critics. Assume that they have a preference for thinking in the positive sense. If you have a justification that would probably convince them that negative logic is more clear than positive logic, then go right ahead. It is probably the right choice.

The moral of this story is of course:

Think Positive


May 2008