LBYL vs EAFP in Java?

Question:

I was recently teaching myself Python and discovered the LBYL/EAFP idioms with regards to error checking before code execution. In Python, it seems the accepted style is EAFP, and it seems to work well with the language.

LBYL (Look Before You Leap):

def safe_divide_1(x, y):
    if y == 0:
        print "Divide-by-0 attempt detected"
        return None
    else:
        return x/y

EAFP (it’s Easier to Ask Forgiveness than Permission):

def safe_divide_2(x, y):
    try:
        return x/y
    except ZeroDivisionError:  
        print "Divide-by-0 attempt detected"
        return None

My question is this: I had never even heard of using EAFP as the primary data validation construct, coming from a Java and C++ background. Is EAFP something that is wise to use in Java? Or is there too much overhead from exceptions? I know that there is only overhead when an exception is actually thrown, so I’m unsure as to why the simpler method of EAFP is not used. Is it just preference?

Asked By: ryeguy

||

Answers:

Personally, and I think this is backed up by convention, EAFP is never a good way to go.
You can look at it as an equivalent to the following:

if (o != null)
    o.doSomething();
else
    // handle

as opposed to:

try {
    o.doSomething()
}
catch (NullPointerException npe) { 
    // handle
}

Moreover, consider the following:

if (a != null)
    if (b != null)
        if (c != null)
            a.getB().getC().doSomething();
        else
            // handle c null
    else
        // handle b null
else
    // handle a null

This may look a lot less elegant (and yes this is a crude example – bear with me), but it gives you much greater granularity in handling the error, as opposed to wrapping it all in a try-catch to get that NullPointerException, and then try to figure out where and why you got it.

The way I see it EAFP should never be used, except for rare situations. Also, since you raised the issue: yes, the try-catch block does incur some overhead even if the exception is not thrown.

Answered By: Yuval Adam

Exceptions are handled more efficiently in Python than in Java, which is at least partly why you see that construct in Python. In Java, it’s more inefficient (in terms of performance) to use exceptions in that way.

Answered By: mipadi

If you are accessing files, EAFP is more reliable than LBYL, because the operations involved in LBYL are not atomic, and the file system might change between the time you look and the time you leap. Actually, the standard name is TOCTOU – Time of Check, Time of Use; bugs caused by inaccurate checking are TOCTOU bugs.

Consider creating a temporary file that must have a unique name. The best way to find out whether the chosen file name exists yet is to try creating it – making sure you use options to ensure that your operation fails if the file does already exist (in POSIX/Unix terms, the O_EXCL flag to open()). If you try to test whether the file already exists (probably using access()), then between the time when that says “No” and the time you try to create the file, someone or something else may have created the file.

Conversely, suppose that you try to read an existing file. Your check that the file exists (LBYL) may say “it is there”, but when you actually open it, you find “it is not there”.

In both these cases, you have to check the final operation – and the LBYL didn’t automatically help.

(If you are messing with SUID or SGID programs, access() asks a different question; it may be relevant to LBYL, but the code still has to take into account the possibility of failure.)

Answered By: Jonathan Leffler

In addition to the relative cost of exceptions in Python and Java, keep in mind that there’s a difference in philosophy / attitude between them. Java tries to be very strict about types (and everything else), requiring explicit, detailed declarations of class/method signatures. It assumes that you should know, at any point, exactly what type of object you’re using and what it is capable of doing. In contrast, Python’s “duck typing” means that you don’t know for sure (and shouldn’t care) what the manifest type of an object is, you only need to care that it quacks when you ask it to. In this kind of permissive environment, the only sane attitude is to presume that things will work, but be ready to deal with the consequences if they don’t. Java’s natural restrictiveness doesn’t fit well with such a casual approach. (This is not intended to disparage either approach or language, but rather to say that these attitudes are part of each language’s idiom, and copying idioms between different languages can often lead to awkwardness and poor communication…)

Answered By: Jeff Shannon

Consider these code snippets:

def int_or_default(x, default=0):
    if x.isdigit():
        return int(x)
    else:
        return default

def int_or_default(x, default=0):
    try:
        return int(x)
    except ValueError:
        return default

They both look correct, right? But one of them isn’t.

The former, using LBYL, fails because of a subtle distinction between isdigit and isdecimal; when called with the string “①²³ ₅”, it will throw an error rather than correctly return the default value.

The later, using EAFTP, results in correct handling, by definition. There is no scope for a behavioural mismatch, because the code that needs the requirement is the code that asserts that requirement.

Using LBYL means taking internal logic and copying them into every call-site. Rather than having one canonical encoding of your requirements, you get a free chance to mess up every single time you call the function.

It’s worth noting that EAFTP isn’t about exceptions, and Java code especially should not be using exceptions pervasively. It is about giving the right job to the right block of code. As an example, using Optional return values is a perfectly valid way of writing EAFTP code, and is far more effective for ensuring correctness than LBYL.

Answered By: Veedrac
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.