4 ms·
The opposite to exceptions isn't (necessarily) error codes. Golang errors are a recent example.
by jwilliams 7y ago
The opposite to exceptions isn't (necessarily) error codes. Golang errors are a recent example.
- tomp 7y agoHow does that work? I haven’t seen the recent changes so I can’t say for sure, but AFAIK Goland and Rust error handling mostly amounts to line noise trying to recreate the control flow of exceptions (i.e. bubbling any errors up the call stack) (although Rust tries to make it more palatable using macros).
- wizzwizz4 7y agoThen don't try to recreate the control flow of exceptions. Handle things ASAP; only propagate an `Err` if the function returning a `Result<_>` logically has an error condition. Don't be afraid to panic, unless user data is on the line, in which case register an unwind handler and tiny backup facility if you don't trust your programming ability (hint: nobody should trust their programming ability that much unless they've rigorously (mathematically) proven their code's soundness). It's a different way of programming, but a more explicit one, and I don't think implicit Bad Thing™ should be easy in a language. (By the way, Rust has a single-character operator to emulate exception pass-through.)
- tomp 7y ago> Don't be afraid to panic, unless user data is on the line, in which case register an unwind handler Sounds like exceptions.
- loopz 7y agoSecondary return values. It works by being explicit about code paths and error contexts. You lose some convenience and should design simpler, more straight-forward code structure.
- erik_seaberg 7y agoExceptions make f(g(), h()) straightforward. All the alternatives are much harder to read.
- loopz 7y agoGolang level is a tad below functional style or business rules.
- notacoward 7y agoIt's only straightforward if you omit the error handlers (which is misleading) or don't handle errors anywhere (which is sloppy). Maybe it works for some kinds of programming, but for any kind of systems code it's a pretty strong smell.
- giovannibajo1 7y agoThe big difference is that in Rust control flow is still explicit. All functions returning errors must declare so with Result, which in turns makes it impossible to forget to handle it on the call site. This forces you to think: what should I do with this Result error code? Should I propagate it or handle it? This thinking is good and produces good design and good error handling. For beginners it is very useful, that, for instance, any function doing I/O does return a Result: it forces you to think how your system should behave in case of I/O error. In C++ or Python, exception control flow is implicit. All functions could theoretically partecipate in an error chain and you have no idea what, if and why a function will raise an exception. Functions doing I/O are completely indistinguishable from the purest functions, and only experience tells you where and if errors should be handled. The consequence is that it’s very hard to find mature code based with sensible error handling. I should notice that Go is basically the same here: the main difference is that it’s more verbose in error propagation, and requires a configured environment (linter errcheck) to detect when functions only returning err are called without error handling/propagation. Rust is superior here, but Go has the same basic architecture, just less evolved.
- AstralStorm 7y agoYou would think just being explicit would produce good design but what actually happened in my experience is that errors are handled by ignorance, as they get hard to pass through. Exactly the same problem as throws clause in Java. Forcing a global (on thread or coroutine level) handler is better, as actually doing that will mean you have to structure the application in a particular way to handle anything, and that structure makes error handling actually easier. Just make it fail to compile if the correct handler is not being installed and ensure every error is identifiable. This is how hardware works anyway internally - interrupt vectors. Having a library to help with correct return and handling of error state is enough. Allowing return only to place that caused the error is saner than what C++ and Java do. There would be no temptation to pass the buck and you would have to model error state to be fully visible, enforcing it to be debuggable and part of explicit API. You could consider it syntactic salt much like Rust's memory management is.
- tsimionescu 7y agoGolang errors are fancy error codes. They have almost all the same problems, except that they at least don't rely on carefully curating error numbers. They still can't tell you the call stack, you still need to propagate them manually, they are still very easy to accidentally ignore (especially on functions which don't return anything else), they still add huge amounts of boilerplate, making it hard to read the code and distinguish <happy-case logic> from <regular error-bubbling logic> from <the places where errors are actually handled>.
- apta 7y agogolang errors are very badly designed. If you want better designed errors-as-return values, check out Rust or Zig.