This hits at something fundamental about Go, which is what I like the most about it... It's a language intended to have few primitives with an emphasis on code being transparent and errors being values, requiring you to think about what they might be at each point as you're forced to carry them up the chain. Do I particularly like managing errors that way? No, but I do think that it improves the transparency and qual…
> Do I particularly like managing errors that way? No, but I do think that it improves the transparency and quality of a lot of Go projects. So long as we can all agree that it feels super bad, I guess this is fine. But it does sort of mean that Golang approaches the Java world back with checked exceptions where principle trumped ergonomics. That lead to a world where folks felt "forced" to use Java, and that's a sti…
Actually, I don't think everyone agrees it feels super bad. I personally like having all of my error handling be explicit, painfully explicit even.
>approaches the Java world back with checked exceptions where principle trumped ergonomics.
I also have to disagree here. To me, checked exceptions are the worst of both worlds. Here you have additional language features, but nearly the same verbosity. Your 'happy path' must be surrounded by try blocks. Worse, everything that happens in a try block is 'flattened.' Not only does this mean you may need many try blocks, sometimes it can even be difficult to take individual statements and put them in one try block. For example:
try {
doThing(doOtherThing());
} catch(...) {
// What happens if doThing and doOtherThing throw the same exception? Do I have to use a temporary variable?
}
Also, exceptions have been overloaded to handle everything, including runtime errors. I think this is more subjective but I strongly dislike it. I do think runtime errors should be possible to handle, just ideally through a separate, more explicit paradigm. try {
doThing(blah[0]);
} catch(...) {
...
} catch(IndexOutOfRange) {
// Handling runtime errors at the same level as application-level errors!!
}
Go doesn't even really force you to check your errors, it just makes it harder to accidentally not check them. Like: result, err := doThing(); // Error
return result
result, _ := doThing(); // NOT an error
return result
I think this is excellent. It may lead to complaints about an annoying compiler, but most importantly it leads to fewer mistakes. You can still explicitly tell the compiler to shut up, but it is obvious.Language ergonomics are complicated, but the benefits of Go's approach are hard to deny. Unfortunately, nothing comes without downsides, and it seems like solving the error handling ergonomics issue is a tough one. I think repetitiveness aside, the Go error handling ergonomics are great, and that's exactly why they attempted to reduce repetitiveness. But, in reflection, a lot of that repetitiveness can also be reduced by refactoring your code, so it may not even be quite as bad as it seems.