Look Before You Leap vs Easier to Ask For Forgiveness Than Permission in Programming
Let’s have a different perspective about this debate and choose which one is better
Okay, that’s a mouthful title. This debate is like a lifelong war for programmers (a bit of exaggeration 😁). Some people prefer to use Look Before You Leap (LBYL), others think that Easier to Ask For Forgiveness Than Permission (EAFP) way better. Then there is some portion of us think “Well, that depends”.
Before jumping directly to code examples, let’s have a brief description of these two approaches.
Look Before You Leap means that we have to check for errors or edge cases before the code goes further. For example, we stop the execution of code if a function returns an error (or unexpected result), then it returns prematurely to the caller.
Whereas for Easier to Ask for Forgiveness than Permission, while the code is running then an error occurred, that error is captured and handled.
It’s like in Look Before You Leap, you restrict that error from happening in the first place by cutting the execution flow. But in Easier to Ask for Forgiveness than Permission, you let that error happen, then if there is an error, you’ll handle it.
By saying that, these two methods actually have their advantages. In my personal opinion, they should be combined by following the idiomatic way a programming language is favoured on. For that purpose, let’s look at the idiomatic way to use them.
In Python, if you want to use LBYL you’ll have a code similar to this.
In this code, we need to check if there are keys for name and price in the dictionary. If there isn’t, then the code runs the else statement.
Now, compare it to the EAFP approach. Which one do you think is better?
If you remove the key-value pair of price, the except statement will run. If there is no error, then the try statement will work as usual.
In general, Python community prefers Easier to Ask for Forgiveness than Permission. Because let’s say that your code has many conditions that could go wrong. If you’re using Look Before You Leap, then you’ll have a lot of if-else statements for checking edge cases. Remember that a bunch of if-else statements will make your code relatively slower compared to try-except handling, but running an exception handling will cost you more compared to a single if-else statement (you can compare them using
timeit). Other than looking at the performance, EAFP has a way nicer syntax. Especially, if you want to reduce your code complexity.
Okay, but when should I choose EAFP and when LBYL?
In most cases, you should use EAFP. That’s the idiomatic way of error-handling in Python. But if you think that your code won’t have too many edge cases and will be simpler with LBYL, that’s a good sign to use one. Remember that exception handling is expensive, so if your code has a chance to throw exceptions a lot, then it’s better to use LBYL.
Does it mean that I will use EAFP a lot in Python? Yes, more likely. So it’s important to understand how to use it properly. The key idea to remember is don’t use general exception handling, but be specific.
From the above example, the exception gets thrown when we’re trying to access an unspecified key in a dictionary. So instead of using Exception (or even BaseException), instead you should specify KeyError in the except statement.
Contrary to Python, the idiomatic way of error handling in Go is Look Before You Leap. In fact, there is no try-except or try-catch in Go. You just have the ol’ if-else statement.
Yes, that’s true that we’ll likely have a long and verbose code, but is this really a downside for Go? Not entirely, that’s how Go was designed. Just like I’ve mentioned in the previous section, exception handling is expensive. If we need to handle a lot of exceptions, then our code will be relatively slower. In addition, Go’s designers prefer explicit error handling.
Take a look at this example:
Typically, functions in Go return a pair/tuple; the first one is the result and the second one is the error. If there is an error, the execution flow stops and returns to the caller. If there is no error, the flow continues.
But is there a way for me to write EAFP in Go? Well, you can (sort of). Peter Verhas can give you a good snippet to do that!
Even though you can write EAFP in Go, it doesn’t mean that you have to use it unwisely. In general, you should stick to using LBYL because that’s the idiomatic way. If only you have some very specific case that requires this approach, you can use it. But still, be mindful of your solution.
One additional note, while in most cases you’ll use LBYL, just like in Python, you need to know how to use it properly (I also provide additional resources if you want to know error handling best practices in the resources section). In Go, there is a renowned term called The Happy Path. The structure would be, you have a function call that returns value and error. One line below the function call, you have error handling and you need to handle this error gracefully, not just checking if there is an error. Also, you always need to handle the error first. Let the happy scenario is following it if there is no error.
If you look closely, it has a pattern of top-to-bottom execution. There is no jumping between control structures. This is what makes Go’s code verbose and explicit.
Let’s have an example.
From the above code, we’re using Axios as the HTTP client. In this case, if
localhost:3000 returns not ok HTTP status (HTTP code 3xx, 4xx or 5xx), the exception handling gets run. But if you want the same behaviour just like in Python, you have to use if-else statements to handle exceptions specifically.
I am pretty sure that this debate is just a matter of personal preferences (and a few technical details). Just like when you have to decide which shirt is better for you; it differs from person to person. I think that having this debate is actually counterproductive. Instead of bringing this into a religious debate, why we’re not having a productive discussion to decide which one to use or even better let’s use them both based on the implementation details.
Other than that, we should stick to being idiomatic when we’re writing code. Why? I’m pretty sure that if we’re learning a language, we’re trying to sound like native speakers. We won’t learn merely the language, but also their culture. To embrace that language better we should think like the natives.
What do you think?
Thank you for reading and happy coding!