Sunday, August 1, 2010

Contextual Indent

There are many different coding conventions that have developed over the years to make coding easier, faster, and intuitive. Amongst these convention lies an important component: indentation.

As is the case with many standards and conventions, indentation is not immune to debates and criticism. Whether you are a spacer or a tabber you have undoubtedly felt strongly about the respective conventions, but this indentation style will cater to spacers - or people who use spaces to indent code. Finally this will show that there is a good reason to use spaces as opposed to just it being a personal preference.

In this article I'm introducing a convention I've perfected over my many years of programming in both back-end as well as front-end technologies. The name I've assigned it is: contextual indent. The reason will become clear below.

Ultimately the purpose of this indentation style is to let the code guide your eye intuitively and attach instinctual sense of what it sees without having to put much thought into it.

What Is Indention

In an earlier article I had introduced my arguments for using spaces instead of tabs to indent your code. I went a step further by suggesting one space is all you need. Not four, not two, but only one space to demarcate indentation. This will prove useful for this indentation style.

Before we continue however we should take a minute to clarify what an indentation represent. Having a solid understanding of this concept is important to be able to agree with anything else in this article. If you disagree with the fundamental concept, you will not understand the benefits of this indentation style.

An indentation in code represents a relationship in the abstract sense. It indicates that whatever is indented belongs to or is controlled by whatever it is relatively indented to.

if (true)
 {
  alert('hello');
 }

Above we can see that the braces belong to the if statement, and the alert statement belongs to the braces that is in turn controlled by the if statement. We can see a hierarchy. This is the same hierarchy we want to preserve aesthetically in our code by way of indentation.

Contextual Indentation Guidelines

There are several guidelines that should be followed to successfully implement this indentation style. The best way to illustrate these is by way of examples.

Braces

In programming braces often denote a block of code, and sometimes even scope. Wherever braces are used we can follow the same indentation convention. This applies to functions, if statements, loops, with statements, and all other language specific functionality that involves braces.

A function can also represent a method, subroutine, class, or any other derivative that is similar in concept to a function. These are all indented in the same way.

function myFunctionName(args
 {
  ....
 }

We can see that the opening brace is on its own line as is the closing brace. This will allow for the opening and closing braces to line up perfectly, and give a sense of scope immediately and visually. This will become more important as one develops advanced code where anonymous functions may get defined anywhere in the code.

function myFunctionName(args)
 {
  return (function ()
           {
            ....
           });
 }

The above shows an anonymous function being returned. We can see that the function's braces are still indented with one space relative to the context (the return function's definition). This is how we derive the name of this indentation style.

if (condition)
 {
  if (second condition)
   {
    ...
   }
  else
   {
    ...
   }
 }
else
 {
  ...
 }

The last example shows how a nested if statement is supposed to be indented as well as illustrate the earlier point that the same style applies to any construct that involves braces regardless of whether it is an if statement or function.

Ternary Operator

Many times we will want to use a ternary operator (?:) instead of an if statement. This is usually the case during variable assignments.

var myVariable = (condition)
                   ? true
                   : false;

The above example shows how the condition is on its own line, and the actual operators are lined up underneath each other. This alignment is most useful when we have nested ternary operators, but is also useful to immediately see the alternative paths visually.

var myVariable = first condition
                  ? second condition
                     ? true
                     : false
                  : false;

The above is very clear. Next I will show you how it would look if we flattened it.

var myVariable = first condition ? second condition ? true : false : false;

Even with this overly simplified example it is still difficult to follow the logic of this nested ternary operator in flattened view. It becomes clear that taking the extra time to indent the code properly yields great savings when it comes to reading your code, and more importantly when having others read your code.

Objects

The beauty of Object Oriented Programming is the ability to interconnect different objects to alter the final behavior. This beauty can quickly become a long-winded chain of objects that can be difficult to read.

objectA.objectB.objectC.objectD.methodDA(paramA,paramB).objectE(params).methodEA(paramA,paramB)

While the above example is fictitious, it is actually shorter than many real-life examples may be. The point is that it can become difficult to read.

Using contextual indentation the rule becomes to have one object/method per line with the exception of the first one. Re-writing the above example results in the code below.

objectA.objectB
       .objectC
       .objectD
       .methodDA(paramA,paramB)
       .objectE(params)
       .methodEA(paramA,paramB)

As you can see, we align the dots. In languages that use other signs, such as Perl or PHP that use arrows (e.g. ->), we would simply vertically align those.

The reason we want to align the dots up front is because it suggests what follows is related to what's before it.

The above illustrates how to properly indent the objects. We observe another data set that causes the code to look to busy: Arguments.

Arguments

As with objects arguments can quickly become a long list of information that can be difficult to visually separate. The contextual indent provides a solution for this as well.

function(paramA,paramB,paramC,paramD,paramE,paramF,paramG);

Again, the above does not illustrate a real-life example, but it could very well be similar to the above. Furthermore, in the case of OOP it is possible to have several chained objects or methods that take parameters as shown in the example above for Objects.

Re-factoring the above would yield the output below.

function(paramA,
         paramB,
         paramC,
         paramD,
         paramE,
         paramF,
         paramG);

Unlike objects we can see that in this case we don't align the commas up front. As a matter of fact the above is just coincidentally that the commas are vertically aligned, but depending on the length of the parameter this would not be the case, nor is there any visual value to doing so. As a result, we just keep the commas at the end regardless of how they appear visually.

Again, unlike objects the parameters or arguments are not related to each other - it's just a list.

Fixing the objects example would yield the output below.

objectA.objectB
       .objectC
       .objectD
       .methodDA(paramA,
                 paramB)
       .objectE(params)
       .methodEA(paramA,
                 paramB)

It become obvious real fast what belongs to what at first glance - unlike its serialized predecessor.

Having said all of this regarding arguments, keep in mind that most of the time this can be avoided by the use of HARP.

1 comments:

dennis said...

It's about time you put this down on ...er paper.

Post a Comment