Flat is better than nested: bail out early

January 26, 2010

Programs must be written for people to read, and only incidentally for machines to execute.

Abelson & Sussman, SICP

There’s a programming paradigm which I shall call, for the lack of a better name, bail out early. It’s so trivial that it almost doesn’t deserve a name, not to mention a blog post. Yet I often come across code that would be so much clearer if bail out early was used. Consider some code like this:

function transmogrify(input) {
    if (input.someConditionIsMet()) {
        var result;
        result = processInput(input);
        if (result) {
            return result;
        } else {
            throw UNDEFINED_RESULT;
        }
    } else {
        throw INVALID_INPUT;
    }
}

There’s a lot of if/else going on here. In the bail out early paradigm, you would try to write this without any else clauses. The trick is to sort out the problematic case first:

function transmogrify(input) {
    if (!input.someConditionIsMet()) {
        throw INVALID_INPUT;
    }

    var result;
    result = processInput(input);
    if (!result) {
        throw UNDEFINED_RESULT;
    }
    return result;
}

See how much flatter the structure of that program is? There are some other advantages:

  • Because you no longer put the main flow inside if statements, your programm is often easier to refactor. If for instance the sanity checks occur in multiple places of your program, you can simply factor them out into a utility function without messing up the structure of your code.
  • You generally have to indent less. And when you move code around, you have to reindent less — particularly pleasant when you’re coding in Python where indentation is significant.
  • In languages with curly braces like my fake JavaScript above, you don’t have to worry about blocks spanning many many lines, thus putting the opening and closing brackets so far a part that you no longer can tell what the closing bracket is actually closing.

11 Responses to “Flat is better than nested: bail out early”

  1. Ian Bicking Says:

    This reminds me of the distinction between this code:

        for (item in loop) {
            if condition(item) {
                do stuff
            }
        }

    and:

        for (item in loop) {
            if not condition(item) {
                continue
            }
            do stuff
        }

    I’m going to use braces because I have no faith in the formatting being preserved.

    • philikon Says:

      Yes, that’s another good example.

      P.S.: I’ve put some <pre>s around your code to preserve the formatting.


    • When it’s this simple I actually prefer the first form, especially when it’s written in python

      for item in loop:
          if condition(item):
              do stuff
      

      and:

      for item in loop:
          if not condition(item):
              continue
          do stuff
      

      And let’s not forget there’s a third form of the original example too:

      function transmogrify(input) {
          if (input.someConditionIsMet()) {
              var result;
              result = processInput(input);
              if (result) {
                  return result;
              }
          throw INVALID_INPUT;
      }
      

      Which to me at least seems simpler.


      • The obvious mistake with the braces clearly shows I’ve been programming in Python too long😉

      • philikon Says:

        Well, your third form isn’t functionally equivalent because you assume it’s the same exception that will be thrown in both cases.

        Ignoring that, I think your form is indeed preferable to the first one I gave because it avoids else clauses. Those tend to make code quite hard to read when the preceding if contains pretty much the whole function.

        I still prefer my 2nd form because it keeps the main flow of the program at a flat level. Your third form still goes one block deeper with every check that’s made. Flat is better than nested🙂

      • alex dante Says:

        For the simple cases, I prefer to shift the condition ‘out’ of the loop:


        filtered_loop = (i for i in loop if condition(i))
        for item in filtered_loop:
        do stuff

        The genexp doesn’t have to be on a separate line, obviously, but I tend to find it clearer this way.


  2. Some people are taught in programming classes that it’s “bad’ to return from a function before the end. I think that’s what leads to this kind of code.

    It’s even more acute in Python by the way. If you have six levels of nested if/else statements, it’s hard to see what’s going on because you have to judge the indentation whitespace to match up an else or elif with its if.

    Martin

    • Godefroid Chapelle Says:

      I would say that more than 3 levels of nested statements should be split in separate functions/methods in most cases… because I think a call to a function with a well chosen name is easier to read than comments nested in those nested statements.

  3. José Dinuncio Says:

    And you can go further with:

        function transmogrify(input) {
            var result;
    
            meetCondition(input);
            result = processInput(input);
            thereIs(result);
            return result;
        }
    
        function meetCondition(input) {
            if (!input.someConditionIsMet()) {
                throw INVALID_INPUT;
            }
        }
    
        function therIs(value) {
            if (!value) {
                throw UNDEFINED_RESULT;
            }
        }

    Ok, you can call me crazy now. I’ve learned that writting little trivial functions like these make my code easier to read and understand.

  4. José Dinuncio Says:

    …but indented.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: