Golang Try() and Programming in the Large
The new try() proposal is counter to the core principles of the Go language, specifically software engineering in the large. The proposal’s stated goal is to reduce the tediousness around handling errors. I believe, that in actuality, it would reduce explicitness, increase complexity, and hinder large teams on large code bases.
try() removes the shape of current error handling, the if != nil
pattern of
error checking and replaces it with a single builtin function, that can be
nested. Using try() would remove the visual pattern that if != nil
imprints
on our code. We now would have to focus more as we read a block of
code, paying special attention to any nested try() statements, to build our
mental model of the functionality we are reviewing. The possible patterns we
need to scan for has now doubled.
The familiar shape of if != nil
offers us a quick way to pattern match on
portions of the code that we can overlook on our first pass. Allowing us to
focus closer on the rest of the code that is itself unique to it’s specific
functionality, and demands closer inspection for full understanding. Adding
try() would require more attention, more thought and effort, be expended in
understanding the code under view.
One of the important goals of Go’s error handling is to actually handle errors. To deal with them when they happen, where they happen.
The language's design and conventions encourage you to explicitly check for errors where they occur (as distinct from the convention in other languages of throwing exceptions and sometimes catching them).
It is possible, when using the try() pattern, to clean up state on an error before the return by using a defer() to handle the code. This, once again, is counter to idiomatic Go, it would now discourage dealing with errors when they happen and where they happen. In addition, this also forces the use of named returns.
Currently with if != nil
there is one way to test, catch, and handle errors,
the try() proposal adds another. Dave Cheny, on the Go Time Podcast, along with
the other Go Time hosts, pointed out that try() is not appropriate for all error
use cases, even those where we receive an error and want to return nil, err
.
Now, during code reviews, we will expend time debating, arguing, and
bikshedding, the more correct way to handle the error.
Even an individual developer, writing a block of code, must now pause and contemplate which error handling pattern they will use. During the Go Time podcast Marcel van Lohuizen said that programmers would need to express (use or have) self control when deciding to use or not use try(), as it would not be applicable in all situations when an error is returned.
Some might see this option as a good thing, which it might be for one developer on one project that they maintain, but it is counter to Go’s core goal of Software engineeing in the large.
Go’s goal of software engineering in the large is to enable large companies, with many many developers, to all work on a huge codebase quickly and easily, removing issues that create traditional friction points that slows development. One of these is choice. That is why Go, as a language, is so small. There are not dozens of different ways to do one thing. This helps reduce all of the points I just mentioned, cognitive load on matching multiple patterns, time spent on dissecting choice in a code review, and stopping to think about which boilerplate best works in a new situation.
The try() proposal, if implemented, would slow larger team and impact their ability to ship software. Programming in the large should not rely too much, or too often, on the self control of programmers to always make the right decision in all situations. There are plenty of other opportunities for programmers to be lax with self control, there is no need to add try() to the pile.
The try() proposal springs from a noble goal, it’s intentions are good, but fall far afield from the deep roots that are entwined throughout the Go language, and the community that has grown up around it. I foresee it complicating code and confounding teams, compounding complexity, and enabling conflict. The Go language would be best without it.