handling more than just errors in go
Recently a draft proposal for additional syntax to aid error handling in the go programming language was published.
TLDR: The proposed syntax implicitly makes error
exceptional, and the problem should be seen as a more general problem, not just error handling.
I want to first address the handle
and ignore the check
.
What’s the problem with this code:
Ignoring the subtle problems for now, the ugly problem is that we’re repeating 3 lines of code over and over. The “normal” way to refactor a bunch of common code lines is with a function.
Maybe something like this:
The biggest, most obvious problem here is that the return
operates within the scope of checkErr
and not does not actually return from CopyFile
. The draft proposal addresses this by creating a new construct handle
for creating custom chunks of code to deal with the common pattern if err != nil { ... }
.
I feel that the proposal violates some go principles:
- errors are not exceptional
- explicit is better than implicit
- language features should be orthogonal to maximize benefit and minimize special cases
The proposal makes error
exceptional by creating a special language construct to deal with this type differently than other types. The proposal creates an additional layer of implicit complexity by burying the if err != nil
construct. The language extension cannot be used for anything other than checking error
values.
Consider this example:
If something like the naive function based approach above were possible, this case could also be handled by something like:
I believe that a more general construct to handle the more general problem of repeated chunks of code would address “the error handling problem” while not being exceptional to error
.
For instance, suppose a new keyword makro
was introduced. A makro
is very much like a func
, except that it executes “in place” rather than within a new function scope. It’s a function that is always inlined.
And the non-error example becomes:
Clearly this could be a much more generally useful construct than handle
. This approach can also handle more specialization, per invocation.
It’s easy to imagine a lot of other cases where this could help reduce code line count. If this construct is limited to being defined within a func, it will not create too much mayhem. It might also be considered allowable at the package level, but definitely seems like something that should not be exported outside a package.
check
I very much like the idea of the new check
keyword, but again, I feel it is overly specific and could be more general. Why must it be tied to only handlers and the error
type?
How about something like:
This is a function that takes any number of inputs, of any type, except the last must be of type error
. It returns those items, and in this case, returns the error, too.
Or how about this:
Combining this with the previous makro
construct, we could do this:
chaining
The usefulness of the chaining idea in the proposal is undeniable. Personally, I lean to explicit over implicit. Having the previous two (more orthogonal) proposals in place allows for a variations on chaining, and some additional options. Having named “handlers” allows for being explicit about how chaining happens, and allows more control within the function of which handler is used in which place.
conclusion
What I’ve outlined here is not coherent enough to be a real proposal, but I think it suffices to demonstrate that there are potential solutions that are more general, solving “the error handling problem” and adhering closer to go language principles.