Go: Error handling
I love Go for many reasons, but this part is still itching me: I postulate that this Go idiom is a burden on our mental capacity:
if err != nil {
return nil
}
Error handling is at a tension between two different developer needs.
-
On the one hand, error handling is very annoying and distracting when working on a constructively formulated use case.
-
On the other hand, not dealing with it correctly means that the program blows up in unknown ways when errors happen: data may be lost, I/O may be half done, features may stop working without the developers noticing.
Go’s explicitness about error handling makes sure that errors are not falling through the cracks as easily as they might do in other languages where errors are implicitly propagated1. While many developers love Go’s explicitness, it is rather verbose for error handling, which has drawbacks:
(1) Humans are pattern matching machines. Whenever we see text
whose shape resembles the error checking idiom, we have trained
ourselves to quickly look away, at the more important code next to it.
But bugs can hide even in repetitive parts of the code. If we’re not
looking at the idiom anyway, the try()
proposal might be the better
option, as it de-duplicates the check.
(2) The error handling pattern takes much visual space. The idiom is necessarily interleaved with the code on the happy path. In many cases, the flow of reading a longer function is interrupted multiple times with the same 3-line code snippet.
Two different hats
I believe that reasoning about constructive code and reasoning about error handling code are two separate states of mind, and switching back and forth between these is straining and tedious2.
A consequence of this is: It might be more productive to spend less time on errors during an initial implementation, and to then address the error handling aspect in a separate pass over the code. It would be better if we could have two views of the code for these different modes of operation:
-
For the feature-focused mode, we need a view of the code where the error handling stays explicit3, but goes out of the way visually in order to make the constructive code more prominent.
-
For the error-focused mode, an error-only view is not really possible because errors happen only as a result of constructive operations, but in many editors, an interactive search for the keywords
return
andtry
(?) will highlight the possible exit points quickly.
I think this would have been well solved with Robert Griesemer’s
try()
proposal. I’m
still sad it didn’t go forward, but I’m glad the Go team is so careful
about it.
Just because we have all already invested so much time into accepting this verbose way of error handling, that doesn’t mean it’s a good thing4.
-
This is particularly true for languages with exceptions where all function calls may implicitly throw exceptions and bubble them up the stack. ↩︎
-
It’s the same as trying to write a scientific article straight in LaTeX form: You can somehow do it, but the first formula will derail your train of thought so hard that the article will probably suffer from it. It’s a better option to write the article in a different medium and then do the translation to LaTeX in a separate step. ↩︎
-
Explicitness is a stated goal in Go, after all. ↩︎
-
The sunk cost effect is strong for programming languages! But then again, it’s much worse for C++. :) ↩︎