Live data from Hacker News

Exploring Error Handling Patterns in Go

8thlight.com

41–50 of 80 posts

Re: Exploring Error Handling Patterns in Go

#41
post #19

Earlier quoted context omitted.

The flipside of easy-to-learn is there's no payoff for getting better with the language. Your code will always be exactly as tedious as novices' code because they'd rather conserve compiler cycles than spend them to amplify programmers' work.

On the bright side, your code will always be as easy to understand as a novice's code too, so it conserves other programmer's mental cycles as well.

This is the root of the Go confusion.

The code will indeed still be easily understandable on a line-by-line basis, but larger units (functions, groups of functions, modules) then become harder to read and understand due to large amounts of noise. As you progress along to more advanced, larger codebases, with novice-style code you just pile boilerplate on top of boilerplate.

Larger Go codebases remind me of old versions of Java where they went as far as to embrace all the boilerplate and call it 'design patterns'. It's sad how Go designers recognized that Java code tends to be hard to read and generally bad, but apparently weren't really able to discern why that is. They decided to just blame it all on inheritance and exceptions.

(FTR I'm not advocating for inheritance or exceptions here, but believing that those are the root of Java's problems and simply omitting them will somehow magically make a language better is just naive & shortsighted.)

Re: Exploring Error Handling Patterns in Go

#42
post #37
post #6

Earlier quoted context omitted.

I don't understand. Go errors out on unused imports, but you can type "import _ foo.com/unused-import" to not error out. Why doesn't 'errors.New("asdf")' error out and require you to instead write '_ = errors.New("asdf")' to ignore the result I think the real answer is not that it's intentional design, but rather that the original compiler was not powerful enough to implement that feature easily... and once go hit 1.…

I don't think it's because of the complexity. I'm quite sure it would not have been difficult to do. One of the reason is that you don't always want to check the error. The most common one is fmt.Println. I would not like to always write _, _ = fmt.Println("Hello, playground")

You certainly do not always want to write _,_ = fmt.Println("Hello, playground"), and I think this points out the real lack. What do you want your program to do if fmt.Println starts failing? I think, unless you are explicitly checking for errors, that in all other cases you want it to crash. Rather than silently continue. Which is a bug, and a potentially disastrous one, that is endemic in Go code (and, to be fair, plenty of other languages). Thankfully the practical risk of this particular case is tiny.

This is why you want unchecked errors to implicitly bubble up. Which is what exceptions give you, or perhaps a syntax with implicit error return values rather than Go's by-convention approach.

Re: Exploring Error Handling Patterns in Go

#43

Earlier quoted context omitted.

The flipside of easy-to-learn is there's no payoff for getting better with the language. Your code will always be exactly as tedious as novices' code because they'd rather conserve compiler cycles than spend them to amplify programmers' work.

That's a feature, not a bug. It means I don't have to deal with anyone's 'clever' code. It's not about saving the compiler work at all, it's about saving the hundreds of humans who have to read your code after you the work of understanding the abstractions you created.

It's always strange watching programmers defend go's obvious deficiencies. I mean, this sort of "appeal to simplicity" could be used to defend anything.

The reality is most go programs are (1) very difficult to understand because error handling swamps their logic and (2) end up reinventing exceptions anyways, albiet poorly and (3) inevitably end up leaking resources because go's "error handling strategy" doesn't ensure resource cleanup.

We can observe this and measure this quite clearly in non-trivial go codebases.

Eventually the go dictatorship will relent and provide exceptions. At that point all the people who praise the existing broken model will happily praise the new approach and denounce the existing brokenness.

Re: Exploring Error Handling Patterns in Go

#44
post #7
post #4

if err != nil return err if err != nil return err if err != nil return err https://github.com/docker/cli/search?q=%22if+err+%21%3D+nil%... https://github.com/kubernetes/kubernetes/search?q=%22if+err+... https://github.com/coreos/etcd/search?q=%22return+err%22&uns... https://github.com/influxdata/influxdb/search?q=%22if+err+%2... The reality of Go's error handling is that you just implement exactly what exception bubb…

This is not correct. Exceptions do different things than report an error. They unwind the stack. That's why they are called exceptions and not errors. One important benefit of Go's error handling pattern is readability. With exceptions, it's not easy to see who handles it and where. There is indeed less code, and that's nice for the writer, but from the reader perspective, error handling becomes obscure. And from the…

The parent is correct. Returns also unwind the stack.

> One important benefit of Go's error handling pattern is readability

I beg to differ, Go's approach is similar to checked exceptions, Java's original sin. And just like checked exceptions, forcing the invoker of a function to handle the error directly is the wrong approach in the vast majority of cases. It just produces code noise and catch/wrap/throw style code, commonly found in old Java enterprise projects. This obsfucates the default path and makes middleware very hard to write.

> With exceptions, it's not easy to see who handles it and where.

Making errors part of the function signature encourages developers to handle them directly at the call site. Which is where most buggy and unreliable error handling is found. The default approach of safely unwinding the stack until you reach the http handler (or equivalent), returning 500 applies to error codes as well. It should be simple to do, automatic even, so novice programmers write robust code out of the box. Hence exceptions.

Re: Exploring Error Handling Patterns in Go

#45
post #35

Earlier quoted context omitted.

Easy, just assume it throws. That's the case anyway. Thanks to panics, even in Go. Edit: Also, there is no parallel control flow. Languages with exceptions have union-type return values, and every statement is implicitly followed by the equivalent of: if err!=nil return nil, err. The fact that in Go you have to type that makes Go cumbersome, not smart.

Not really, in all the years I've been writing Go, only one library used panics for error handling. Usually if something panics you don't want to handle it. (Other than at the http handler level, where you can just throw an InternalServerError and log the panic) First and foremost, you can usually assume libraries won't panic, though it would be nice to have a tool (grep) to check for explicit panics.

If something "errors", you usually don't want to handle it either. Other that at the http handler level.

Re: Exploring Error Handling Patterns in Go

#46
post #43

Earlier quoted context omitted.

That's a feature, not a bug. It means I don't have to deal with anyone's 'clever' code. It's not about saving the compiler work at all, it's about saving the hundreds of humans who have to read your code after you the work of understanding the abstractions you created.

It's always strange watching programmers defend go's obvious deficiencies. I mean, this sort of "appeal to simplicity" could be used to defend anything. The reality is most go programs are (1) very difficult to understand because error handling swamps their logic and (2) end up reinventing exceptions anyways, albiet poorly and (3) inevitably end up leaking resources because go's "error handling strategy" doesn't ensu…

I wholeheartedly agree with you. Go got a lot right (concurrency, deployment), but some parts of Go's language design are missing the last two decades of programming language history. To me, arguments supporting Go's error handling approach alway seem a little bit like people are rationalizing a horrible mistake.

Re: Exploring Error Handling Patterns in Go

#47
post #35
post #26

Earlier quoted context omitted.

I've always been annoyed by the parallel control flow introduced by exceptions in any language. They are used so often in many languages where it doesn't feel necessary. The fact that I don't even have to think if the function call I'm looking at can throw and if I should catch it or not outweighs everything.

Easy, just assume it throws. That's the case anyway. Thanks to panics, even in Go. Edit: Also, there is no parallel control flow. Languages with exceptions have union-type return values, and every statement is implicitly followed by the equivalent of: if err!=nil return nil, err. The fact that in Go you have to type that makes Go cumbersome, not smart.

I'm not sure I understand the relationship between union-types and exceptions. You mean something like ? Could you expand on that?

> Thanks to panics, even in Go.

Fortunately, for some reason Go developers don't use panic like exceptions and recover them at library boundaries.

Re: Exploring Error Handling Patterns in Go

#49
post #25

Earlier quoted context omitted.

Just using WithStack() from "github.com/pkg/errors" on any error that originates from outside my repository has been my go-to rule for any Go project. It has never disappointed.

Like an exception?

Except it's just a returned value and not a goto lookalike.
Post reply on HN