Hacker News new | ask | show | jobs
by tylerhou 2605 days ago
> 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 <- computeFirst
       second <- computeSecond
       return $ f first second
and

    first, err := computeFirst()
    if err != nil {
        return nil, err
    }
    second, err := computeSecond()
    if err != nil {
        return nil, err
    }
    return f(first, second), nil
Even without do-notation the Haskell is considerably shorter:

    computeFirst >>= (\first ->
    computeSecond >>= (\second ->
        return $ f first second))
Agreed about exceptions, though; I dislike them as well.
2 comments

> 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.

The last example is Haskell without the syntactic sugar. The first example is in Haskell how it's normally written, and it's far clearer than Go.
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?

In the error monad above, computeSecond will not be called if computeFirst failed. But in other monads (which have the same syntax) computeSecond MAY be called depending on the rules of composition.

return is a bit of a misnomer in Haskell -- it really means "wrap this value in the monad supplied by the context." So as other commenters have mentioned, f(...) cannot fail. Non-monadic Haskell functions never use return.

If f could fail and you indeed wanted to propagate its error if it did fail, you could write the code as

    do first <- computeFirst
       second <- computeSecond
       f first second
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.
This version of f can't fail, so "return" wraps its value in the same monad. Normally the "do" block would end by getting a monad from f, just as the Go version would normally let f return an error.