Hacker News new | past | comments | ask | show | jobs | submit login

In Java I feel like most of the time it's fine to just Throw (or let throw) errors, and a parent can catch and log and move on with its life.

Go feels like making _all_ exceptions checked, whereas I usually try to only use unchecked exceptions and it seems to work out fine :P

I think that the difference between the Go way and exceptions is that exceptions are “automatic” and thus invisible. In a language with exceptions what you get up-stack is probably something like:

  FooException at /path/to/useless/file_1.x:42
While in properly-wrapped Go code you might get something like:

  performing request "abcd-1234" on host "pickles-1":
    requesting "storage-4":
    querying database "primary":
    not found
And yes, you can attach code location information as well. See, for example, how Rob Pike et al. do error handling in their Upspin project[1] or the popular github.com/pkg/errors module[2].

Can you do that with exceptions? Of course! The problem is that suddenly your code will start to look a lot like "if err != nil".

[1] https://commandcenter.blogspot.com/2017/12/error-handling-in...

[2] https://godoc.org/github.com/pkg/errors

> I think that the difference between the Go way and exceptions is that exceptions are “automatic” and thus invisible

That exceptions automatically take care of propagating context is a big plus.

> See, for example, how Rob Pike et al. do error handling in their Upspin project[1]

The fact that there needs to be an entire blog post just to explain how to use errors in golang properly is quite telling about how poor of a job it does. Not to mention all the hoops they have to jump to propagate useful information, all of which we already have in languages with exceptions.

Java is probably the worst language for error handling. It has checked exceptions AND unchecked.

I can't say I've ever seen a Java codebase that handles InterruptedException properly. It's so bad that most Java devs I've talked to don't even know what the right thing to do is.

And don't get me started about catching Throwable. Why make it so easy for application code to catch VirtualMachineError. I've not once seen Throwable or Error caught in a way that makes any sense.

Java seems to encourage a culture of not handling errors properly because it looks "messy". Then, these same acculturated devs try to bring that misguided sensibility about how code should look to Go and start complaining that they actually have to handle errors.

Yeah, Java botched generics by not adding exception sum types. stream.map(f).collect(…) should throw whatever f can throw, but instead f has to wrap any checked exception, which makes them nearly unusable in practice.

But the drudgery of explicitly passing every error up the stack over and over is not a good use of human beings' time. We can generate that if we must, but not actually read it.

> not adding exception sum types What's this mean?

Ideally we want something like

  class Stream<T> {
    <R, X> Stream<R> map(
      Function<? super T, ? extends R, throws X> mapper
    ) throws X
and if you pass a function that throws, say, IOException and SQLException, then X=IOException|SQLException and that's what that call to #map throws.

You can sort of do it by http://natpryce.com/articles/000738.html but you have to choose one base type and you can't make stdlib support it.

Sure it works fine until you start multithreading, where handling an exception doesn't end with unwinding the stack, because there are multiple stacks. So then have to pass it as a value to some other thread's stack, and remember to catch it in every thread or else your thread will silently die. But then you're right back to where Go is: treating errors as values.

You simply have a single uncaught-exception handler and that takes care of the problem that you mention.

Instead of 500,000 if err != nil checks littering the source code and making functions unreadable.

I’m well aware of how to deal with the problems created by unchecked, stack-unwinding exceptions. The thing is, when I’m reading Go code, I can look at a call site and easily know if an error can occur and what happens to the error (including if it’s sent to another goroutine), using very simple and consistent mechanisms. The tradeoff is verbosity, but it is worthwhile verbosity IMO because it’s simple, consistent, explicit, and does not create the unnecessary coupling of checked exceptions. I appreciate this low cognitive overhead when I go back to a codebase 6 months later, or when onboarding a new hire.

At least you can't mistakingly ignore errors, unlike in a lot of golang code I've seen where errors are either ignored, or worse, silently overwritten.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact