Hacker News new | ask | show | jobs
by grey-area 101 days ago
I agree go’s error handling feels a bit clunky, though I prefer the local error handling and passing up the chain (if it were a bit more ergonomic) to exceptions, which IMO have a lot of other problems.

The main problems seem to me to be boilerplate and error types being so simplistic (interface just has a method returning a string). Boilerplate definitely seems solvable and a proper error interface too. I tend to use my own error type where I want more info (as in networking errors) but wish Go had an interface with at least error codes that everyone used and was used in the stdlib.

My rule of thumb on annotation is default to no, and add it at the top level. You’ll soon realise if you need more.

How would you fix it if given the chance?

2 comments

> I agree go’s error handling feels a bit clunky

It should be the same handling as all other types. If it feels clunkier than any other type, you've not found a good design yet. Keep trying new ideas.

Well two things to me feel clunky, first is less serious but leads to lots of verbosity:

1. if err != nil is verbose and distracting and happens a lot. I'd prefer say Ian Lance Taylor's suggestion of something like this where you're just going to return it vs standard boilerplate which has to return other stuff along with the error:

// ? Returns error if non-nil, otherwise continue

data, err := os.ReadFile(path) ?

// Current situation

data, err := os.ReadFile(path)

if err != nil {

  return x,y,z,err
}

The second is a problem of culture more than anything but the stdlib is to blame:

2. The errors pkg and error interface has very basic string-based errors. This is used throughout the stdlib and of course in a lot of go code so we are forced to interact with it. It also encourages people to string match on errors to identify them etc etc. Yes you can use your own error types and error interfaces but this then creates interop problems and inevitably many pkgs you use return the error interface. I use my own error types, but still have to use error a lot due to stdlib etc. The wrapping they added and the annotation they encourage is also pretty horrible IMO, returning a bunch of concatted strings.

So these are not things that end users of the language can fix. Surely we can do better than this for error handling?

> if err != nil is verbose and distracting and happens a lot.

if err != nil is no more or less verbose than if x > y. You may have a point that Go could do branching better in general, but that isn't about errors specifically.

If there is something about errors that happening a lot then that still questions your design. Keep trying new ideas until it isn't happening a lot.

> Surely we can do better than this for error handling?

Surely we can do better for handling of all types? And in theory we can. In practice, it is like the story of generics in Go: Nobody smart enough to figure out a good solution wants to put in the work. Google eventually found a domain expert in generics to bring in as a contractor to come up with a design, but, even assuming Google is still willing to invest a lot of money in the new budget-tightening tech landscape, it is not clear who that person is in this case.

Ian Lance Taylor, as you mention, tried quite hard — with work spanning over many years — in both in both cases to find a solution, which we should commend him for, but that type of design isn't really his primary wheelhouse.

> if err != nil is no more or less verbose than if x > y. You may have a point that Go could do branching better in general, but that isn't about errors specifically.

In practice though, there's not nearly as many cases where someone needs to repeat `if x > y { return x }` a bunch of times in the same function. Whether the issue is "about errors" specifically doesn't really change the relatively common view that it's an annoying pattern. It's not surprising that some people might be more interested in fixing the practical annoyance that they deal with every day even if it's not a solution to the general problem that no one has made progress on for over a decade.

> there's not nearly as many cases where someone needs to repeat `if x > y { return x }` a bunch of times

In my evaluating of a fairly large codebase, if err != nil makes up a small percentage of all if statements. I think you may have a point that branching isn't great, but I'm still not sure trying to focus that into errors isn't missing the forest for the trees.

> it's an annoying pattern.

But, again, if it is so annoying, why is it the pattern you are settling on? There are all kinds of options here, including exception handlers, which Go also supports and even uses for error handling in the standard library (e.g. encoding/json). If your design is bad, make it better.

> It's not surprising that some people might be more interested in fixing

If they were interested in fixing it, they'd have done so already. The Go team does listen and has made it clear they are looking for solutions. Perhaps you mean some people dream about someone else doing it for them? But, again, who is that person going to be?

Philip Wadler, the guy who they eventually found to come up with a viable generics approach, also literally invented monads. If there was ever someone who might have a chance of finding a solution in this case I dare say it is also him, but it is apparent that not even he is willing/able.

> In my evaluating of a fairly large codebase, if err != nil makes up a small percentage of all if statements. I think you may have a point that branching isn't great, but I'm still not sure trying to focus that into errors isn't missing the forest for the trees.

I don't agree with the premise that a frustrating pattern has to comprise a large percentage of the instances of the general syntax for people to want to change it. I can tell you do, but I don't think this is something that people will universally agree with, and I'd argue that telling people "you can't have the opinion you have because it doesn't make sense to me" isn't a very effective or useful statement.

> But, again, if it is so annoying, why is it the pattern you are settling on? There are all kinds of options here, including exception handlers, which Go also supports and even uses for error handling in the standard library (e.g. encoding/json). If your design is bad, make it better.

Empirically, people don't seem to think they have better options, or else they'd be using them. If you try to solve someone's problem by giving them a different tool, and they still say they have the problem even with that, you're probably not going to convince them by telling them "you're doing it bad".

> If they were interested in fixing it, they'd have done so already. The Go team does listen and has made it clear they are looking for solutions. Perhaps you mean some people dream about someone else doing it for them? But, again, who is that person going to be? > Philip Wadler, the guy who they eventually found to come up with a viable generics approach, also literally invented monads. If there was ever someone who might have a chance of finding a solution in this case I dare say it is also him, but it is apparent that not even he is willing/able.

I'd argue there have been plenty of solutions for the specific problem that's being discussed here proposed that are rejected for not being general solutions to the problem that you're describing. My point is that there's a decent number of people who aren't satisfied with this, and would prefer that this is something solved in the specific case rather than the general for the exact reason you pointed out: it doesn't seem like anyone is willing or able to solve it in the general case.

My point is that I think a lot of people want a solution to a specific problem, and you don't want that problem solved unless it solves the general problem that the problem is a specific case of. There's nothing wrong with that, but your objections are mostly phrased as claiming that they don't actually have the problem they have, and I think that's kind of odd. It's totally fair to hold the opinion that solving the specific problem would not be a good idea, but telling people that they don't care about what they care about is just needlessly provocative.

In an HTTP server, top level means the handlers, is that so?
Yes I guess I do annotation in two places - initial error deep in libraries is annotated, this is passed back up to the initial handlers who log and respond and decide what to show users. Obviously that’s just a rule of thumb and doesn’t always apply.

Depends if it can be handled lower (with a retry or default data for example), if it can be it won’t be passed all the way up.

Generally though I haven’t personally found it useful to always annotate at every point in the call chain. So my default is not to annotate and if err return err.

What I like about errors instead of exceptions is they are boring and predictable and in the call signature so I wouldn’t want to lose that.