I Like Whitespace

I Like Whitespace

By Bob Schmidt

Overload, 23(125):12-16, February 2015


Programming style can cause endless arguments. Bob Schmidt shares why he thinks whitespace matters.

This article contains sarcasm and (possibly failed) attempts at humor. My sarcasm font is broken, so you’re just going to have to recognize those sections without any help from me.

I know that nothing attracts more heat than light than discussions about programming style. Where do the braces go; how many spaces to indent; in C++ where should the const keyword be placed. We all have opinions on all of these subjects, and more. Most aspects of programming style are matters of taste, and taste is subjective. My style is, of course, the only correct one.

I use a lot of whitespace when writing code. From reviewing and maintaining a lot of code written by others I know that this is not a common practice. I think it is an issue of weak thumbs and pinky fingers. Later I’ll suggest exercises for strengthening these digits so that inserting additional whitespace (in the form of spaces, carriage returns and (temporary) tabs) will become easier.

Motivation

Back in the mid-1980s I had the misfortune of working on a system that used RATFOR, a language created by Brian Kernighan that is a pre-compiler for FORTRAN 66 [ Kernighan76 ]. RATFOR provides modern programming structure syntax to FORTRAN 66, ostensibly making it easier to write good code than straight FORTRAN.

A code beautifier was part of the RATFOR software tool set. Running a source file through the beautifier generated a hard-copy listing of the source, with indentations based on the source syntax. It was an OK tool for the time, although it didn’t always represent the true structure of the code correctly.

Unfortunately, the presence of the beautifier gave some of the original programmers on the project ‘permission’ to not structure their source code. Open a source file on a programmer’s terminal, and every line started in the far left-hand column. (See Listing 1.)

IF(A<B) THEN
[
IF(B<C) THEN
[
CALL X(A)
]
ELSE
[
CALL Y(B)
]
ENDIF
]
ELSE
[
CALL Z(C)
]
ENDIF
			
Listing 1

This was perhaps the ugliest code I have ever had the misfortune to work on. I would completely format the entire source file prior to trying to fix or enhance the code. (I have not yet recovered from the trauma, in spite of all the software conference ‘therapy’ I’ve attended in the intervening years.)

It was working on this ugly RATFOR code that first got me really thinking about coding style. When I first started programming, back in the dark ages of 1981, any resemblance my code had to a style was strictly an accident. I like to think my coding style has evolved a lot since then. Today I prefer a style that showcases the code and its structure, so I don't have to go looking for it.

Spaces

Iwouldn’treadalineoftextinanarticleifitwaswrittenlikethis. So why should I have to read a line of code formatted like this:

   for(int i=0;i<someMaxValue;++i)

I believe it is so much easier to read if it is written more like this:

  for ( int i = 0;   i < some_max_value;   ++ i )

Note the extra spaces: on either side of the opening parenthesis; on both sides of the assignment and less than operators; and multiple spaces after the semicolons. The multiple spaces after the semicolons make each of the sections of the for statement stand out.

For more complex cases, separate the three sections of a for statement to separate lines (see Listing 2).

for ( int i = 0, j = 0;
      i < some_max_value   &&   j < some_other_max_value;
      ++ i, ++ j )
			
Listing 2

Long iterator type names can lead to long for statements; the multi-line version of the for statement is particularly useful there. (Note that the new auto keyword should help alleviate some of those problems.)

I see a lot of code where an array index is itself an index into an array – sometimes to a third level. (See first line in Listing 3.)

array1[array2[array3[index3].index2].index1]

x = array1[ array2[ array3[ index3 ].index2 ].index1 ]
			
Listing 3

I find this difficult to parse mentally. When writing array subscripts, I put a space after the opening bracket and one before the closing bracket (see second line in Listing 3).

Admittedly, it is clearer to pull the nested indices out into their own variables, but I still like to put spaces around the index:

  index2 = array3[ index3 ].index2;
  index1 = array2[ index2 ].index1;
  x      = array1[ index1 ];

For complex Boolean expressions I tend to use extra spaces to separate sub-expressions:

  if ( ( x < 0 ) || ( y > 0 ) )

At times I’ll use extra spaces instead of parenthesis:

  if ( x < 0   ||   y > 0 )

I find either of the previous versions easier to parse than either of these options:

  if((x<0)||(y>0))

or

  if(x<0||y>0)

If you continue reading you will notice that I also put a space after a function name and before the opening parenthesis of the parameter list. This is a holdover from my FORTRAN days (which weren’t that long ago) when I used that space to differentiate between a function name (with the space) and an array name (without the space).

  X = SINE ( ANGLE )
  Y = ARRAY( ELEMENT )

Blank lines

Like spaces, I use blank lines liberally in my code. I use a blank line to separate variable declarations from other statements in a function, particularly in C where the declarations typically occur at the beginning of a function, or the beginning of a block. I also use a blank line to visually separate different steps in the code, such as the setup of a function call and the call itself.

  message.header = TYPE;
  message.body   = contents;
 
  status = send_message ( &message );

A line with nothing but a curly brace is a blank line for this purpose.

  if ( something )
  {
      do_something_else ();
  }

Putting the brace on the line after the if statement provides visual separation for the then clause.

Alignment

I find it easier to read code when certain aspects of the code are aligned. Consider variable definitions:

  int x=0;
  short longer_name=1;
  char short_name[2]={0,0};

I find this style easier to follow:

  int   x               = 0;
  short longer_name     = 1;
  char  short_name[ 2 ] = { 0, 0 };

Aligning types, names, equal signs, and initial values makes each section of a definition stand out. The example here is trivial, but recently I’ve been working on some legacy C code with functions that have 20 or more definitions at the beginning of each function. My eyes started to water when I had to find a particular entry in the list. (It’s fixed, now.)

When making multiple assignments, I like to align the equals sign (see Listing 4).

structure.element_the_first = 1;
structure.second_element    = 2;
structure.third_element     = function_that_returns_the_needed_value ();
			
Listing 4

Complex Boolean expressions are a place where spacing and alignment can come into play at the same time.

Who can look at this mess and easily tell what’s going on?

  if(((x==0)&&(x==1))||((y==3)&&((z==5)||(z==6))))

Using a combination of spacing and alignment makes it a little easier to parse mentally:

  if ( ( ( x == 0 ) && ( x == 1 )      ) ||
       ( ( y == 3                 ) &&
         ( ( z == 5 ) || z == 6 ) )    )    )

Camel case

I don’t like writing or reading names in camel case. iWouldn’tReadTextIfItWasWrittenLikeThis, so why should I have to read variable and function names written this way? We can’t embed spaces in names (unless you’re still writing in FORTRAN, which deletes whitespace prior to parsing), so the best we can do is use the underscore. some_max_value is easier to read than SomeMaxValue , and the longer and more descriptive the name the easier it is.

Braces and indentation

I used to think that three-space indentation was just about perfect. One or two spaces aren’t enough visually, and four or more pushed code way too far to the right, particularly on old 80-character wide TTY terminals.

  if ( x == 0 )
     {
     process_x_when_0 ();
     }
  else
     {
     process_x_when_not_0 ();
     }

Note that using three space indentation, and indenting the braces, causes the if and else blocks to line up under the opening parenthesis of the if statement (assuming you put a space between the if and the opening parenthesis). I found this visually appealing, and I wrote a lot of code that way.

I’ve since adopted a four space indentation, with the braces lined up with the if and else keywords, for one major reason – it is the common indentation used by my major customer (they have no standards), and it is easier in this case to adapt than be the odd man out. Plus, taking the advice of Bob Martin, I don’t write functions with large, heavily indented if - then - else trees anymore [ Martin09 ]. (But note that Bob is against the aligning rules I use, so he’s not right about everything .)

  if ( x == 0 )
  {
      process_x_when_0 ();
  }
  else
  {
      process_x_when_not_0 ();
  }

I really don’t like this style:

  if ( x == 0 ) {
      process_x_when_0 ();
  }
  else {
      process_x_when_not_0 ();
  }

I like putting my opening and closing braces on separate lines, for two reasons: they are always easy to find, and the line with nothing on it but the brace provides a line of whitespace around the statements that make up the then and else clauses.

MISRA standards [ MISRA13 ] require that braces be used around if and else clauses even if there is only one statement in the clause (as in each of the three previous examples). I try not to write code that looks like this:

  if ( x == 0 )
      process_x_when_0 ();
  else
      process_x_when_not_0 ();

The standard is meant to eliminate problems introduced in maintenance:

  if ( x == 0 )
      process_x_when_0 ();
  else
      process_x_when_not_0 ();
      process_something_else ();  // ALWAYS EXECUTED

Or with nested if statements (I have seen an error of this type just recently):

  if ( x == 0 )
      if ( y == 0 )
          process_x_and_y_are_0 ();
  else
      process_x_is_not_0 ();

This only works the way you want it to with properly applied braces:

  if ( x == 0 )
  {
      if ( y == 0 )
      {
          process_x_and_y_are_0 ();
      }
  }
  else
  {
      process_x_is_not_0 ();
  }

I admit that I do, sometimes, omit the braces, based on the context of the code. I’m working on forcing myself to always include them.

Editor's note: The two listings of above example have been corrected with respect to the article in the printed magazine.

Tabs

If you don’t want to over-work your thumbs hitting the space bar, by all means use the tab key, but please, set your editor to replace tabs with spaces. Two things frustrate me when it comes to the tab character remaining in code. I have to figure out what tab setting was used to begin with (seconds of my life I will never get back), and if I’m editing a line and delete a tab I now have to re-add spaces I may not have wanted to delete.

const

Where, exactly, should const be placed in a definition or declaration?

The most common placement of const is (was?) as in Listing 5.

const int        i;    // i is a const int
      int* const p1;   // p1 is a const ptr to int
const int*       p2;   // p2 is a ptr to a const int
const int* const p3;   // p3 is a const ptr to a const int
			
Listing 5

Dan Saks is an advocate of placing const in a way that makes it easy to read the declaration, when read from right to left [ Saks88 ] (see Listing 6).

int  const        i;   // i is a const int
int* const        p1;  // p1 is a const ptr to int
int  const*       p2;  // p2 is a ptr to a const int
int  const* const p3;  // p3 is a const ptr to a const int
			
Listing 6

In a conversation with Dan, I mentioned that I was using the other style, because in general I didn’t find it difficult to parse mentally. Dan explained to me that I wasn’t the person I should be writing it for, and I’ve used his style ever since. (I can be persuaded.)

So what does this have to do with whitespace? As you can see, in either style I like to line up the const keywords in a block of declarations. I think it’s an important part of a declaration, and the alignment makes the const (or lack of it) pop out. (And line up the comments, too; it makes them easier to read.) I treat the volatile keyword the same way.

Function parameters

I write function prototypes like this:

  int function_name ( int        first_parameter,
                      double     second_paramter,
                      CLASS_NAME third_parameter );

When calling a function I will format it in a similar way. Having each parameter on a separate line makes them easy to distinquish from one another, and it is easy to add a comment to a parameter (which I find particularly useful when calling one of Microsoft’s multi-parameter SDK functions):

  int result = function_name ( first_parameter,
                               second_parameter,
                               class_parameter );

There are some exceptions. For functions with only two parameters, I tend to put them on the same line as the function name; two parameters are not hard to pick out on one line (Listing 7).

int result = function_name ( first_parameter, second_parameter );
			
Listing 7

I do the same thing for standard library and STL functions and member functions, regardless of the number of parameters, because the parameter lists for these functions tend to be well known.

In his presentation ‘Seven Ineffective Coding Habits of Many Programmers’, Kevlin Henney [ Henney14 ] talks about “ unsustainable spacing ” (approximately minute 24), and rejects the idea of certain styles of aligning code as being unmaintainable. He makes the point that maintaining certain styles (mine being one) is “ doomed to failure ” unless the code never changes. He uses as an example changing the name of a function.

Changing the name of the function breaks the alignment (see Listing 8).

int  not_aligned_like_this ( int     first_parameter,
                             String  second_parameter )

int  new_function_name ( int   first_parameter,
                              short second_parameter )
			
Listing 8

Kevlin suggests the style in Listing 9, or something similar, because performing a refactoring such as changing the name doesn’t break the alignment.

int  original_function_name (
    int   first_parameter,
    short second_parameter )

int  new_function_name ( 
    int   first_parameter,
    short second_parameter )
			
Listing 9

I see Kevlin’s point, but I’m not … wait, hold the presses.

I have had a rough time with this section on function parameter placement. As I said in my opening note, this is nothing but an opinion piece, and my goal was to document the way I do things, give an explanation as to why I do it that way, and leave it up to you, dear reader, to ignore what I have to say and do it the way you want. That’s fine; I don’t really expect to sway many opinions. (Maybe one person? Anybody? <crickets>)

The problem occurred when I tried to pick a rhetorical fight with Kevlin with regard to placement and alignment of function parameters. I really don’t like the style Kevlin advocates in his presentation. There is something about starting the parameter list on the line after the function call I find aesthetically unpleasing. I tried five or six times to justify using my style, and not his, but I kept running into this wall.

With all due respect to Kevlin, just how often is a function name changed over a large code base? I don’t work on any code base that easily allows that type of change. Changing a widely used function name isn’t something to be taken lightly, and if you do it you should be prepared to do it correctly. (OK, Bob, but what are you going to do when you start working on a code base that does allow those types of changes, and actively encourages them?)

I see Kevlin’s point, but using the shortcomings of our current tool set to advocate use of such an ugly style is wrong. (Compromise isn’t a dirty word, Bob.)

And on it went.

In his presentation Kevlin asks a rhetorical question, “ Why do you choose an approach that is difficult to maintain? ” I do it because I feel it is the right thing to do. I truly believe if we’re not actively improving our code we are passively making it worse. I consider it part of doing a good job, and nobody said it would be easy.

Then Kevlin followed up with “ Do your colleagues also do what you do? ” Sadly, the answer is no. And that is where I kept getting tripped up in my counter-argument.

So, after all of the false starts I realized that I do see Kevlin’s point, and most of my justifications boiled down to “this is the way I have always done it, I like it that way, and I don’t want to change”. Not exactly the end to the rhetorical fight I envisioned when I started this section.

Now the question is, will I actually change the way I write function prototypes and calls? The answer is, I don’t know. I will try to come up with a style that satisfies my need for readability and consistency, and Kevlin’s need for sustainability. It's the aesthetic issue that is going to be the problem. (Cue cognitive disconnect in five, four, three…)

Conclusion

Most of us, if not all of us, spend a lot more time reading code than writing it. My chosen style isn’t more efficient on the writing end; on the contrary, I would say that it takes a bit more time. It is not easier to write code in this style – I would say that it requires a bit more discipline. I believe the payoff occurs when the code is read, by myself or by others.

Thirty years ago, when hard drives were expensive, programmers were cheap, and programmer’s terminals had 23 lines and 80 columns, it might have made sense to use the fewest number of characters possible to implement code. Here we are in 2015, where terabyte drives are almost trivially cheap, programmers are expensive, and we are no longer using TTYs. Making use of whitespace to make code easier to read for ourselves and our colleagues makes sense – to me, at least.

Acknowledgements

As always, my thanks to Fran and the reviewers. You read this and published it anyway. And a special thank you to Kevlin Henney, who didn’t volunteer for the rhetorical fight, but easily won it, anyway.

References

[Henney14] ‘Seven Ineffective Coding Habits of Many Programmers’, Henney, Kevlin, NDC 2014 http://vimeo.com/97329157

[Kernighan76] Software Tools, Kernighan, Brian and Plauger, P.J., Addison-Wesley Professional, 1976

[Martin09] Clean Code, Martin, Robert C., Prentice Hall, 2009

[MISRA13] ‘Guidelines for the use of the C language in critical systems’, MISRA, http://www.misra-c.com/

[Saks88] ‘Placing const in Declarations’, Saks, Dan, Embedded Systems Programming , June 1988

Exercises

‘Pinky Finger Workout’, typingweb, https://www.typingweb.com/tutor/lesson/index/id/357/

‘Thumb Exercises: Active Motion’, Northwestern Memorial Hospital, http://www.nmh.org/ccurl/275/700/thumb-exercises-active.pdf






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.