4 ms·
I think this topic has been discussed countless times, and this post fails to add anything worthy to it. My two cents: use result types, or their analog in you
by kaba0 2y ago
I think this topic has been discussed countless times, and this post fails to add anything worthy to it.
My two cents: use result types, or their analog in your language for expected error conditions, and use exceptions for exceptional situations. Example for the former is parsing, which has an expected failure mode - something couldn’t be parsed as intended. Example for the latter is a network issue during an API call/writing a file/etc. Nonetheless, they are a homologous structure to a Result type in more FP languages, so it’s purely which is a better fit for the given circumstance.
I would also like to defend exceptions, as they are a very good construction with sane and safe defaults. They let people concentrate on the actual business logic, not sprinkling little bit of business and error handling logic all over the place; they do the sane thing by default: auto-unwrap the correct result, auto-bubble up if not handled at a given scope; and often unappreciated: they allow as fine- or course-grained error handling as needed, while it is only possible with some less than readable Monad magic with Result types. And most importantly, they never let an erroneous condition silently go unnoticed, you have to deliberately ignore them for that. With errno checking (c, go), and a bit less so with result types you can fall victim to that.
Edit: Oh, and they add stacktraces! That’s also a very important and useful feature of them, much more so than “grepping for this hopefully unique error code in the codebase to find where it might come from with zero further detail”.
- MaxBarraclough 2y ago> They let people concentrate on the actual business logic, not sprinkling little bit of business and error handling logic all over the place This can lead to trouble if the programmer doesn't keep in mind that various (but perhaps not all) operations can throw. In particular, proper behaviour in the exceptional case may require a more specific order of operations than is needed for proper behaviour in the normal case. Raymond Chen did a great blog post on this in 2005, Cleaner, more elegant, and harder to recognize. [0][1] [0] https://devblogs.microsoft.com/oldnewthing/20050114-00/?p=36693 https://devblogs.microsoft.com/oldnewthing/20050114-00/?p=36... [1] https://news.ycombinator.com/item?id=406391 https://news.ycombinator.com/item?id=406391
- taberiand 2y agoThe main problem I have with exceptions is that libraries can't be trusted to only throw exceptions in exceptional situations, or even properly document the situations in which they throw exceptions, so my code must be constantly paranoid about every call into library code. Contrast this with languages that provide result types and the certainty of either handling or passing up the error, particularly when the language provides a clean construct like ? in Rust - I just really like the feeling of certainty I get when writing that code over code that can throw exceptions.
- wvenable 2y agoI just assume every piece of code can throw any kind of exception. If some code doesn't throw an exception today, it might change tomorrow. It ultimately doesn't matter. There is no need to worry about it. There are only key points in any application where you can properly handle errors/exceptions and only certain errors/exceptions that can be handled in a specific way. Neither the location of the handler or can be handled has anything to do with what code triggered the error. Explicitly declaring exceptions/errors completely breaks polymorphism. The library that you call exposes it's internals that way and if those internals change then your code breaks. Either that, or to maintain a consistent API, the library has to lie about the errors it produces by hiding the real error away in a more generic error type.
- taberiand 2y agoI disagree with most of your arguments. I think that exceptions are obviously part of the API - they just tend to be a lot more hidden and less thoughtfully designed than the standard interface. Wrapping or mapping internal exceptions to a API level library-specific type isn't lying, it's just basic data modelling - the same as mapping e.g., a database response to a HTTP response, including database errors to client-appropriate HTTP error codes And error handling can and should frequently be managed at the call site into a library, even for exceptional cases. Consider transient errors like network failures - it's standard to handle and retry at the call site with a delay rather than immediately propagate to the caller. All of this is made simpler and more explicit with the use of Result types or similar over Exceptions. Fundamentally, I think Exceptions don't simplify anything, they just hide complexity - and Result types don't complicate code, they just describe the otherwise hidden complexity.