Although I very much see the reason why it was developed, in effect it comes closer to a glorified form of "come from" (and people thought that goto was considered harmful).
Although I very much see the reason why it was developed, in effect it comes closer to a glorified form of "come from" (and people thought that goto was considered harmful).
Most functions will just pass on exceptions verbatim so it's better than error return values because with them the entire codebase has to be littered with error handling, compared to fewer try catch blocks.
setjmp, etc. are like unchecked exceptions, so I'm also not a fan, but I use this occasionally in C anyway.
Errors as return values also form part of the function signature in many languages.
>Most functions will just pass on exceptions verbatim so it's better than error return values because with them the entire codebase has to be littered with error handling, compared to fewer try catch blocks.
The question is whether you think that calls that might pass an error up the call chain should be marked as such. I think they should be.
I wouldn't call this "littered with error handling" just because a certain language has decided to do this in a way that resembles industrial style fly-tipping rather than just littering.
Rust's Try operator is the minimal ceremony, a single question mark acknowledges that we might not succeed and if so we return early - but there was still ceremony for each such early return.
I happen to think exceptions are inherently a bad idea, but for a different reason.
I've worked with a lot of code like this (particularly C libraries and litanies of return codes), and it's fine... But I prefer something like Java-style exceptions. And with Java lambdas or Kotlin the trend is unfortunately away from checked exceptions these days...
I too am interested in your other reasons!
I believe that for today's large software which has multiple engineers, possibly not even working in the same group, involved in the final product, the key problem of exceptions (knowing what is or is not an "exceptional" situation for the software) is not soluble in the general case. Exceptions are a clever idea if you're a solo writing a soup to nuts piece of software, say the firmware for a thermostat, but are too flawed for the library author or the app developer.
Now the library programmer has to guess whether when you - the application programmer try to Wibble a Foozle - will have ensured all the Foozles can be Wibbled, and so not being able to Wibble the Foozle is an exceptional condition, or whether you want to just try to Wibble every Foozle and get an error return if it can't be Wibbled...
One option is for every such condition you bifurcate the API. Instead of three types with a total of eight methods, maybe there are six conditions for two of those methods, and so now it is three types with over 100 methods... ouch. Good bye documentation and product quality.
Meanwhile, for a function doing numerous file copies and network calls I would throw an exception, as the number of possible failure cases are almost limitless. (Like you surely not want to have an ADT that models FileSystemFullErrors and whatnot).
It so happens that in this particular example one is pure and the other is side-effecting, but I'm not convinced that's a hard rule here.
frob1 :: Foo -> Bar -> R
frob2 :: (Foo, Bar) -> R
frob3 :: FooBar -> R
data FooBar = FooBar { foo :: Foo, bar :: Bar }A function executes, and some error happens:
- Return error value: try to handle the error ASAP. The closer to the error the more detailed the information. Higher probability of recovery. Explicit error code handling throughout the code. Example: maybe you try again in one millisecond because the error is a very low probability but possible event.
- Exception: managing errors requires a high-level overview of the program state. Example: no space left on device, inform the user. You gather the detailed information where the error happened. The information is passed as-is or augmented with more information as it bubbles up the stack until someone decides to take action. Pros: separate error handling and happy path code, cleaner code. Cons: separate error handling and happy path code, unhandled errors.
Worst case scenario: you program in C. You don't have exceptions. You are forbidden to use setjmp because rules. A lot of errors are exposed directly to the programmer because this is a low-level language. You return error codes. Rules force you to handle every possible return code. Your code gets incorporated as an appendix to the Necronomicon.
E.g. a generic `map` function can become throwing if it handles throwing lambdas, but are otherwise non-throwing - this is pretty much the main pain point with Java's checked exceptions.