LBYL vs EAFP

In this article, we will look at the word count problem through the lens of two coding styles: Look before you leap (LBYL) vs Easier to Ask for Forgiveness than Permission (EAFP)

If you are here, you have probably read the word count problem from the previous post. If not, take a look now, because we will be discussing that problem here. We will specifically look at two popular coding styles, called Look Before You Leap and Easier to Ask for Forgiveness than Permission commonly abbreviated as LBLY and EAFP.

Before we discuss the two coding styles, let us recap the steps required to solve the word count problem.

You want to loop through the words and keep track of the count in a dictionary–if the word is already present in the dictionary then increment its count, otherwise add it to the dictionary with a count of one.

As it says above, we need to do a different thing if the word is already in the dictionary or not.

Look Before You Leap

Remember that if we just directly access a dictionary d[key] when the key is not present, we will get a KeyError. Therefore, we need to first check whether the key is present in the dict, and only access the key if it is present.

Here is the code applying this approach

def count_words(line):
    counts = {}
    words = line.split()
    for word in words:
        if word in counts:
            counts[word] = counts[word] + 1
        else:
            counts[word] = 1
    return counts

Here, we check that the word exists in counts, and only then retrieve counts[word]. By doing so, we ensure that we will not get the KeyError. Since we have checks to prevent the error from occurring, this coding style is called Look Before You Leap.

This is a very common approach, especially from developers who have prior experience in languages like Java or C++ where the LBYL style is highly encouraged. In those languages, an error indicates that something has gone wrong with the execution. You are not expected to raise errors during the normal execution of a function (hence why Java's Map classes return null if the key is not present in the map). One of the downsides of LBYL style is that there can be a lot of code that is preventive in nature and the actual business logic can be buried deep inside a lot of ancillary code.

Easier to Ask for Forgiveness then Permission

The alternate coding style is to keep the main "happy path" logic in the front and handle the error if it occurs. Here is what it looks like

def count_words(line):
    counts = {}
    words = line.split()
    for word in words:
        try:
            counts[word] = counts[word] + 1
        except KeyError:
            counts[word] = 1
    return counts

In this approach, we just get the word from the dictionary and increment the count. This is the "happy path" case. If the word does not exist in the dict, then we will get a KeyError which we handle in the next line. This is the basic EAFP approach–we just do the operation that we want to do. If that operation gives an error, then we deal with that.

EAFP is a common paradigm in the python community. Many of the standard library data structures are designed to raise errors even for normal operations, such as the way a dictionary raises a KeyError when the key is not present. Contrast this with a Java Map which returns null becase an error in Java indicates something going wrong.

Hence it is common to see code that just does a bunch of operations without prior checking if they are valid or not, and then handling the errors later on. This has benefits in the readability as the main business logic is not cluttered by all sorts of checks around it.

Another issue that can be (very rarely) encountered is that the state of the system might change between the check and the code. Consider the case where you want to open a file that might not exist. If you use the LBYL approach you get something like this

if os.path.exists(filename):
    f = open(filename)
    # do something with f

There is a rare chance that the file exists when the first line is executed. However, in that millisecond the user or some other script deletes the file. And when the next line is executed the file does not exist any more. So line 1 is evaluated as True, but line 2 gives an error. Granted, it is a rare case that most of us do not need to worry about, but the EAFP approach avoids this possibility by attempting to open the file directly.

try:
    f = open(filename)
    # do something with f
except FileNotFoundError:
    # file doesnt exist

The downside of the EAFP approach is that you are essentially using errors for regular flow control, which can make it harder to visualise the flow paths (especially error flow paths) through a routine.

Comparing the two approaches

A common question is which is better: LBYL or EAFP?

The answer is neither is better or worse as such. It is a matter of coding style and personal preference and it is possible to use any style in any language.

That said, the language design usually tilts one way or the other. Java has been designed to encourage the LBYL style while Python has been designed around EAFP.

In this article, we took a look at how to implement the word count problem using the LBLY and EAFP approaches. But there are better ways to solve this problem than both approaches shown here! That's for the next article.