Thursday, August 18, 2011

Designing Good Interfaces: Function Names, Arguments & Their Actions

Wanted to share an interesting (and very oftenly overlooked) programming practice.  It is a very bright idea, intuitive, almost, I would argue, to have function names describe their actions and/or intentions:
  create_stack(), destroy_stack(), walk_stack(), kill_process().
What these functions might do is almost self explanatory from their names.
This simple (often untold) rule has helped legions of programmers maintain their sanity (at least of whatever is left), because they know that when they call create_stack() it will create stack, NOT destroy it.  And if it does, then there is something obviously wrong with the function.  Easy-Beezee.  Basic programming principle, correct?

So imagine yourself working at the famous LHC and you want to excite an electron!  Luckily for you, you are a programmer! So the herculean task of exciting an electron is reduced to writing a few lines of code for you!
Now say, you are onto writing a subroutine to excite an electron, naturally you will be tempted call your's as some variation of: excite_electron().

Everything excited, has to eventually come down to a ground state, so you want a complimentary function to bring that electron back to the ground state.  You can go with unexcite_electron().  Or you can pass in a TRUE/FALSE argument to the same excite_electron() function and let it decide whether to excite or undo the excitement (pull it to ground state, unexcite).
(Note: Yes, in most cases we programmers couldn't care less if unexcite is not a valid English word, as long as it unambiguously conveys the functionality).

No big deal. Trivial. Programming looks so easy, you say!
Well, then why the hell do so many programmers look eternally grim with unkempt premature white/gray hair? Here's one reason why...
I bet there are many ways to come up with good function name(s), excite/unexcite pair being one (even if they sound so unexciting!).
But here's particularly one sure-shot way to earn the wrath of your fellow-programmers who have to maintain your code and use your (dreaded!) APIs...
 void excite_electron (void* electron, boolean undo_excite)  
 {  
   if (undo_excite) {  
     decrement( electron->energy );  
   } else {  
     increment( electron->energy );  
   }  
 }  
What! Don't see the problem yet!  Wait till you have to invoke that interface to do something:
   // Excite an electron
   excite_electron( &hydrogen_electron, FALSE );  
   
   // Do your fancy experiments  
   
   // Now bring it back to ground state
   excite_electron( &hydrogen_electron, TRUE );  
@#$! Yes, that was my reaction too!  See the problem NOW!
Nothing wrong with the function in itself, but put into larger perspective and it looks dreadful.  NEVER EVER have a function name with one verb/action (excite in this case) and a totally opposite action/verb in the arguments (unexcite).  Not even for obfuscated code!  In my opinion this is plain wrong, not obfuscated.

Just for the kicks... if you want to make it worse, you can go with:
 excite_electron_2 (void* electron, boolean undo_excite)  
 {  
   if (undo_excite != FALSE) {  
     decrement( electron->energy );  
   } else {  
     increment( electron->energy );  
   }  
 }  
- OR -
 excite_electron_2 (void* electron, boolean undo_excite)  
 {  
   if (!undo_excite) {  
     increment( electron->energy );  
   } else {  
     decrement( electron->energy );  
   }  
 }  

If you still haven't evoked the wrath of that seasoned programmer who has, say, assimilated the essence of zen into himself, you can take it a notch higher by declaring the prototype in a header file as:
 // Function prototype to excite an electron, that is bound to get you killed!  
 void excite_electron (void* , boolean);  

And let the caller be totally surprised of what happens next! :)
Exact same function and exact same outcome, but you have just made it a little harder for reader to decode its workings.  Make sure you are not around after you checkin this kind of sh!t into the code repository!

This is NOT out of my figment of imagination, this sh!t happens for real!

Moral:
0. Law of least astonishment: Please use consistent verbs for function names and arguments. This would be the correct function declaration:
     excite_electron( void* electron, boolean excite );  
Now the following invocation tells me intuitively without any ambiguity that I am trying to excite an hydrogen electron even without looking at function prototype or its definition.
     excite_electron( &hydrogen_electron, TRUE );
1. Avoid double negation:
     if (dont_remove != FALSE) { ... }   // not too readable, DON'T do it  
     if (dont_remove == TRUE)  { ... }   // much readable now!  
     if (dont_remove)          { ... }   // not only readable but also elegant!  
2. Designing good usable interfaces is a non-trivial task! Programming is NOT just about churning out lines of code.  It is way more than that! That is why some of us believe programming is not just a science, it is an art!

No comments: