3 ms·
> - Use errcheck and friends to ensure errors are not ignored. - Never dereference the left hand return value if the right hand is an error. This itself is a v
by groestl 3y ago
> - Use errcheck and friends to ensure errors are not ignored. - Never dereference the left hand return value if the right hand is an error.
This itself is a very concise argument for exceptions. Automatically checks errors are not ignored, and don't allow access to the left hand return value.
- ncruces 3y agoBubbling up errors is not handing them. Making bubbling up even easier because people find this too verbose is the original mistake: if err != nil { return err } I find it verbose too. But if the alternative is to design something that allows you to ignore it 99%, and then put a huge syntactic penalty into doing anything other than bubbling up, then people will just bubble up 99.95% of the time, and complain about the other 0.05. I'm all for ensuring errors are not silently ignored. That's Go's mistake. Exceptions are not the answer.
- segfaltnh 3y agoI strongly agree. In principle, exceptions are a streamlined version of tossing err up the stack without any embellishment. In practice, I put cookies away in the cabinet so that I don't eat them. Humans write code, and our behaviors change in response to our surroundings. I think a lot more about failure cases in Go than Ruby. Maybe you're not like that, but you're the exception that proves the rule!
- groestl 3y ago> In principle, exceptions are a streamlined version of tossing err up the stack without any embellishment. This is exactly what I meant. It's "handling", when viewing the consuming function as failing system. It's not "handling" when viewing the application as failing system.
- chuckadams 3y agoChecking for exceptional conditions after every operation sounds an awful lot to me like checked exceptions with extra steps.
- ncruces 3y agoYes. The point is the extra steps mean that the delta between “just bubbling up” and “doing something about it” is smaller, which increases the chance that you'll consider “doing something about it.” If “bubbling up” is infinitely easier than handling it, you'll postpone handling it as much as you can.
- chuckadams 3y agoYet most Go code in the wild does nothing but bubble up the error. Three out of every four lines of code dedicated to doing nothing more than that. If only there were syntax sugar doing so automatically, maybe even a compiler mechanism for forcing you to declare those that you do push up to the caller in such fashion... I don't even particularly like exceptions either, I just like having to implement them by hand even less. Meanwhile, all the Smug Lisp Weenies™ are chuckling and mumbling something about "restarts". What really kills me is that Go has a structured exception mechanism with panic/recover/defer, and it's actually better than try/catch/finally at that. It just doesn't seem to have caught on as the standard exception handling idiom.
- groestl 3y ago> It just doesn't seem to have caught on as the standard exception handling idiom. Not only that, but there is heaps of code which is correct in the presence of errors (after all, Go developers tend to obsess a lot about them), but leaks resources in the presence of panics. Those just fly under the radar of most Go programmers. Good thing Go introduced defer. Because the panic-safe era (similar to the exception-safe era of C++) seems to be an era yet to come for the Go ecosystem.
- groestl 3y ago> you'll postpone handling it as much as you can. Which is exactly what you should do if you are in the game to build reliable complex systems. See my lengthy post here https://news.ycombinator.com/item?id=39808367 https://news.ycombinator.com/item?id=39808367
- groestl 3y ago> Bubbling up errors is not handing them. And yet, this is exactly how you learn to design dependable systems. You try to stay in fail early territory as long as possible (bubbling up errors, failing every system level on the way), until you reach a layer where fail stop is not acceptable (or log and throw is adviseable since you reached a top level system layer). It's at this point where you introduce a resilience layer, sometimes using a library like failsafe. The most horrible designs and buggy systems I've seen in my career where the result of trying to mask errors in random places, making it impossible to reason about any of the code, and leaking resources left and right. The most stable systems I've seen started from a place where _everything_ that went off script resulted in the most horrible stacktraces, but taking proper care about cleaning up resources, and improving from there. Often, the top level resilience layer would only be configured for retries or failovers in (pre-) production systems, so developers and testing would keep getting battered with stacktraces, providing a chance to fix stuff.