Live data from Hacker News

Go 1.13: xerrors

crawshaw.io

111–120 of 150 posts

Re: Go 1.13: xerrors

#111

Earlier quoted context omitted.

> Go's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Not really, it's very annoying to chain calls which can error. Also, as another commenter mentioned, functions which can potentially fail should return sum types and not product types. Compare: do first and first, err := computeFirst() if err != nil { return nil, err } second, err := computeSecond() if err != nil { re…

> Even without do-notation the Haskell is considerably shorter This is a great example, not to your point, but to the methodology of Go. You give a verbose Go example, that I expect the vast majority of readers here could understand, even if they've never written a single line of Go, followed by a terse but syntax-heavy Haskell example that I expect relatively few could.

The Go version is 78% useless noise trying to hide

  f(computeFirst(), computeSecond())
which is what a maintainer needs to focus on. We know everything can fail, we don't need to be incessantly reminded of that.

If completely inexperienced people can read idiomatic code, that means the idioms don't capture anything tricky that had to be learned the hard way. There's no payoff for getting better with the language.

Re: Go 1.13: xerrors

#112
post #75
post #69

Earlier quoted context omitted.

And, like Go, TypeScript was strongly associated with a lead designer whose name is well-known and who shaped the industry over multiple decades.

Except that TypeScript's designer had strong and established experience in language design, and he made a lot of correct choices when implementing TypeScript. Can't say that about golang.

I'm not sure if you're casting aspersions on Rob Pike and Ken Thompson. But they are truly giants of computer science giants and they are behind golang. Can't say that about typescript (or any other language I can think of).

Re: Go 1.13: xerrors

#113

Earlier quoted context omitted.

My takeaway from the last 30 years in computer science is that errors are not exceptional. They occur often and should be accounted for near the code that generates the errors. Exceptions and return values are both sub-optimal. Exceptions encourage drastic actions for non-drastic events (exit the program if an HTTP server is transitently slow). Return values encourage ignoring the error value, and then wondering why…

> My takeaway from the last 30 years in computer science is that errors are not exceptional. They occur often and should be accounted for near the code that generates the errors. This is exactly opposite my experience. In 90% of cases there is no way to recover from an error locally and I should just fail at the highest level, possibly retrying a few times.

If that's correct, then our systems are built on false assumptions. SSDs should not see an ECC error and write the data to a new block; that error should kill your program. TCP should not detect packet loss and retransmit, it should kill your program?

My point is, a lot of good error handling is built at the lowest level, close to the point of failure. That code knows what the problems are and how to fix them. Errors are not the exception, they are the rule. If you handle it well, people won't even know how unreliable the underlying system is. (NAND flash? The first time I used raw NAND flash I was blown away at how unreliable it was. How do computers even work at all like this, I thought. Then I realized... that's why so much money is poured into things like SSD controllers. To hide that fact from the user and make them happy, even if the raw technology it's built from doesn't offer perfect reliability.)

Re: Go 1.13: xerrors

#114

Earlier quoted context omitted.

> And I am so happy that they won't do anything but the right thing. That's clearly not true - they've made plenty of mistakes.

The only big one that comes to mind is GOPATH. What are you thinking of?

No immutability. Nullability by default, and weird corner cases like nil slices and interfaces. Language semantics requiring weird stack discipline that is incompatible with normal ABIs, and causes overhead when you need to interop.

But frankly, just the design of append() alone should be a warning sign. If it were a low-level facility, fine, it's only needed to be understood by whoever's writing a library to wrap it for general consumption. But it's the idiomatic Go way of maintaining a sequential mutable collection! How is such a basic operation is so non-obvious that people still write articles to explain it, and so verbose that you must reference the collection twice to make it work right?

(Indeed, the language itself kinda acknowledges how clumsy it is, by making it an error to ignore the return value of append(). But, as usual with Go, it's hardcoded - i.e. you can't do the same for your own function. And IIRC, it wasn't there from the get go, but got added somewhere along the line.)

Re: Go 1.13: xerrors

#115
post #45

Earlier quoted context omitted.

That's a stawman fallacy. There is no perfect language. However, there are languages that almost are strictly superior to others. Java and C# in this case are almost strictly superior to golang in almost every front.

Well, unless you consider Go's concurrency model superior, as well as the fact that you get native binaries.

Java and .NET also produce native binaries, apparently many still don't bother to learn how.

Re: Go 1.13: xerrors

#116
post #4

This (it looks like to me) is an attempt to pull in the best bits of Dave Cheney's errors package (which I love) into the standard library: https://github.com/pkg/errors Standardising error unwrapping is a great idea IMHO and I think that this has a lot of merit. I don't like the `fmt.Errorf("more description: %w", err)` though for several reasons. Firstly it is a lot more opaque than Dave Cheney's original mechanism…

> Secondly why is this really important functionality in `fmt` and not in `error`? `fmt` already depends on `errors`, and Go does not allow cyclical dependencies, so `errors` would have to reimplement string formatting.

Why? Go permits mutual recursion, and "go tool link" seems to support separate compilation that would resolve that cycle.

Re: Go 1.13: xerrors

#117
post #42

Earlier quoted context omitted.

Go's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Re: exceptions, there are a lot of people, myself included, who do not view exceptions as "advancements", but as setbacks. Magically and suddenly subverting the normal control flow and unwinding the stack in highly concurrent programs (as is expected in Go) is an awful way to do error handling, and you end up having to…

> Go's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Not really, it's very annoying to chain calls which can error. Also, as another commenter mentioned, functions which can potentially fail should return sum types and not product types. Compare: do first and first, err := computeFirst() if err != nil { return nil, err } second, err := computeSecond() if err != nil { re…

Apologies, I know very little Haskell - does that example mean that `computeSecond` will always be called, even if `computeFirst` failed? If it does, it's not the same as the Go code which will not call `computeSecond` in that case (which might be a requirement - who knows?)

I'm also assuming that `f first second` will return (an error monad?) if either of `first` or `second` are (an error monad?) - is that right?

Re: Go 1.13: xerrors

#118
post #65

Earlier quoted context omitted.

My takeaway from the last 30 years in computer science is that errors are not exceptional. They occur often and should be accounted for near the code that generates the errors. Exceptions and return values are both sub-optimal. Exceptions encourage drastic actions for non-drastic events (exit the program if an HTTP server is transitently slow). Return values encourage ignoring the error value, and then wondering why…

Rust's type system + the failure crate ( https://crates.io/crates/failure ) is the nicest I've seen. It's similar to Haskell in that errors are part of the return value of a function, and the type system enforces handling of this. _But_ Rust also includes some really nice syntax for passing errors through so I can write this: use failure::Error; fn foo() -> Result { may_return_an_error()?; might_return_a_different_er…

This sounds exactly like Java's checked exceptions.

Re: Go 1.13: xerrors

#119

Earlier quoted context omitted.

> Go's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Not really, it's very annoying to chain calls which can error. Also, as another commenter mentioned, functions which can potentially fail should return sum types and not product types. Compare: do first and first, err := computeFirst() if err != nil { return nil, err } second, err := computeSecond() if err != nil { re…

Apologies, I know very little Haskell - does that example mean that `computeSecond` will always be called, even if `computeFirst` failed? If it does, it's not the same as the Go code which will not call `computeSecond` in that case (which might be a requirement - who knows?) I'm also assuming that `f first second` will return (an error monad?) if either of `first` or `second` are (an error monad?) - is that right?

It will not, because code inside a do-block is sequenced. The value `first` in the line `first <-computeFirst` is a non-error value. If computeFirst fails, the value does not exist (because computeFirst must return either an error or a non-error value), and so the whole computation fails.

Re: Go 1.13: xerrors

#120

Earlier quoted context omitted.

> My takeaway from the last 30 years in computer science is that errors are not exceptional. They occur often and should be accounted for near the code that generates the errors. This is exactly opposite my experience. In 90% of cases there is no way to recover from an error locally and I should just fail at the highest level, possibly retrying a few times.

If that's correct, then our systems are built on false assumptions. SSDs should not see an ECC error and write the data to a new block; that error should kill your program. TCP should not detect packet loss and retransmit, it should kill your program? My point is, a lot of good error handling is built at the lowest level, close to the point of failure. That code knows what the problems are and how to fix them. Errors…

>My point is, a lot of good error handling is built at the lowest level, close to the point of failure.

This is very true for some types of errors and for some types of programs but not true at all for others, which is why this debate has been going on and on for decades.

An extremely common example for the latter is programs that touch a DBMS or file system on every other line. You don't want to see error handling code for "database is gone" or "local disk is gone" events all over the place.

The information required to handle these errors just doesn't exist in the local context. So the only reasonable question is how to let those errors bubble up to some central location where you can handle them or decide to abort.

In my opinion there are good arguments for and against both exceptions and error returns. But if error returns are used, then there must be some reasonably ergonomic way to do it, i.e not what Go does right now.

Post reply on HN