Exception usage can usually be split into three buckets: (1) programming errors where abort() or equivalent isn't suitable (e.g. libraries); (2) semantic errors at the application level where language provided unwinding is used as a convenience to jump back to the core event / request loop and provide an error to the user or client; and (3) to provide out-of-band error information for failures when interacting with non-deterministic systems (e.g. failure to open a file or communicate with a device, where the natural function to write returns a value, rather than a success or error code).
There are alternative solutions to all three, and all three may not apply to every environment. For embedded systems, case (1) may indeed not apply. But cases (2) and (3) may be useful as a programming convenience to automate the idiom of checking error conditions and aborting the current operation. If checking error conditions and aborting is fully automated (like monadic Result handlers in e.g. Rust) then you start approaching an isomorphic semantics to exceptions, with no necessary difference in implementation details.
nope, definitely not thinking of asserts, they are nothing to do with this situation.
program logic encoded in an exception handler is undeniably less easy to read than explicitly coded error cases.
Also, the non-locality of the decision making means the further away from the error-site you are, the more context you have to keep in your head.
This similar to what deep inheritance hierarchies suffer from, non-local logic. you end up jumping all around your source tree trying to figure out the full context that an error has occurred in.
"program logic encoded in an exception handler is undeniably less easy to read than explicitly coded error cases"
For which category of exceptions? For interaction with non-deterministic systems where you can make a localized decision - and this is fairly rare - I'd agree with you. For all the other categories, I think you're wrong. If you never used exceptions in the other ways, this will of course colour your thinking.
That's a very strong statement. You really believe there can't possibly exist any instance where program logic in an exception handler ends up being easier to read? That assumes you know enough about every possible permutation of logic flow to know that exceptions could not benefit it.
Sure. The real point I'm getting at is that overly strong statements lead to arguments, where people take your statement at face value. Hyperbole is rarely useful in a serious discussion. It just means people have to work to determine your real stance because it may not match exactly what you said.
You could have said "I've never seen a case where exception handling resulted in more clear and easier to read code, and I doubt I'll ever encounter such a situation" and I think that would convey your opinion clearer (assuming I understand it correctly).
> No rules are universal.
We're talking about CS and programming here, where there are plenty of cases where things have been formally proven. Some rules are universal. No reason to use absolutist statements where they don't apple.
Exception usage can usually be split into three buckets: (1) programming errors where abort() or equivalent isn't suitable (e.g. libraries); (2) semantic errors at the application level where language provided unwinding is used as a convenience to jump back to the core event / request loop and provide an error to the user or client; and (3) to provide out-of-band error information for failures when interacting with non-deterministic systems (e.g. failure to open a file or communicate with a device, where the natural function to write returns a value, rather than a success or error code).
There are alternative solutions to all three, and all three may not apply to every environment. For embedded systems, case (1) may indeed not apply. But cases (2) and (3) may be useful as a programming convenience to automate the idiom of checking error conditions and aborting the current operation. If checking error conditions and aborting is fully automated (like monadic Result handlers in e.g. Rust) then you start approaching an isomorphic semantics to exceptions, with no necessary difference in implementation details.