Climb The Branches or Throw ‘em Overboard?

Sunday, April 22, 2007

There’s a simple coding (logical, not syntactic) style difference found when writing code with conditionals. The first form prefers positive checks to screen for error conditions which are handled later in else clauses. The second form prefers negative checks at the beginning of a routine to throw error codes and escape before the body of the routine.

function foo(bar)
{
    if (bar)
    {
        bar.baz = 1;
        // ...

        return bar;
    }
    return null;
}

versus

function foo(bar)
{
    if (!bar) return null;

    bar.baz = 1;
    // ...

    return bar;
}

The second form seems much better suited to the case where you only have a single uncomplicated check, but one could still argue that the first form reads easier. The first form, however, seems obviously better suited to cases where you have several conditionals which depend on one another and operate at varying levels of specificity throughout the data.

Certain programming language cultures sometimes prefer one over the other, though implicitly. Which do you prefer and in which cases? Why?

tagged: asides, software
written by Brad Fults

Archived at: http://h3h.net/2007/04/climb-the-branches-or-throw-em-overboard/

3 responses

  1. D.J. Capelis

    I tend to use both.

    The first style mostly when there’s only one conditional and it’s small straight-line code inside the block, the second at the top of a long block where I check for multiple conditions.

    They compile to nearly the same assembly, both downward facing branches in different locations, branch prediction will work well on both so there doesn’t seem to be much of a performance argument for either style.

  2. Chris Radcliff

    Here’s another vote for both, pretty much in the situations you mentioned.

    More specifically, I put short-circuit tests as close to the top of the block as possible. That way the list of conditions under which the routine will be skipped are made obvious without requiring excessive indentation. It also allows for later improvements to the conditions; for example, raising an error based on a missing value might be replaced with setting that value with a reasonable default.

    if (!bar) return null;
    if (!bar.baztemp) bar.baztemp = 1;
    bar.baz = bar.baztemp;
    //...
    return bar;

    One counterexample is returning the result of the routine without further processing. (For instance, if a set of values is found in a cache.) In that case I prefer to use the first form, because the results may require further processing in a later revision.

    if (!bar) return null;
    result = getcache(bar);
    if (!result)
    {
    // do something expensive
    }
    return result;

  3. Joe Auricchio

    In the compiler for CSE 131B, there were many places where the compiler made many semantic checks. Sometimes I implemented them as nested ifs…

    if(! check A)
    {
        return errA;
    }
    else
    {
        something;
        if(! check B)
        {
            return err B;
        }
    
        something important;
    }
    

    When I did this I pretty much hated my life.

    Elsewhere I did

    if(! check A) { return errA; }
    something;
    if(! checkB) { return errB; }
    something else;
    

    When I did this I didn’t feel happy, but I felt better.

    I think in a lot of cases I’d prefer a more declarative style: either assertions or Perl-style “check or die”.

  4. Comment Preview