Philosophically, I view exceptions as the programmer saying “we have entered a state that we either cannot or will not handle correctly, and so we will punt this back up the call stack, and the caller can decide whether to abort, retry, or do something else”. Frequently, the reason for being unable to handle that state is due to a lack of context—for example, if someone is writing library code that deals with HTTP requests, “what should the progam do in the event of a network issue” is something that the writer of the library cannot answer (because the answer may be different in different contexts). In these cases, punting the decision up the call stack seems to be the obviously correct thing (though there is a bit of a holy war in programming over whether it is better to do this explicitly or implicitly).
Both sides of that holy war will generally agree that thrown exceptions are a slightly odd pattern. In terms of why one might want to use that odd pattern, it’s easiest to see the advantages of the pattern by looking at what happens when you remove it. One alternative to using that pattern is to do what Rust does, and return Result<oktype,errtype> for everything which can fail.
So let’s take an example Rust program which fetches an OpenAPI schema from a server, then makes a GET request against an endpoint, and determines whether the result matches the schema. This is a fairly simple task, and yet with explicit error handling (and without using the try operator, which is Rust’s answer to thrown exceptions) it looks like this. Happy path code is mixed with error handling code, and as such it can be difficult to verify the correctness of either when reading the code.
If you want to argue that the improved happy-path-readability of code which uses thrown exceptions is not worth it, I can get back to you once I finish convincing people that vim is obviously better than emacs.
Programmer by trade here.
Philosophically, I view exceptions as the programmer saying “we have entered a state that we either cannot or will not handle correctly, and so we will punt this back up the call stack, and the caller can decide whether to abort, retry, or do something else”. Frequently, the reason for being unable to handle that state is due to a lack of context—for example, if someone is writing library code that deals with HTTP requests, “what should the progam do in the event of a network issue” is something that the writer of the library cannot answer (because the answer may be different in different contexts). In these cases, punting the decision up the call stack seems to be the obviously correct thing (though there is a bit of a holy war in programming over whether it is better to do this explicitly or implicitly).
Both sides of that holy war will generally agree that thrown exceptions are a slightly odd pattern. In terms of why one might want to use that odd pattern, it’s easiest to see the advantages of the pattern by looking at what happens when you remove it. One alternative to using that pattern is to do what Rust does, and return
Result<oktype,errtype>
for everything which can fail.So let’s take an example Rust program which fetches an OpenAPI schema from a server, then makes a GET request against an endpoint, and determines whether the result matches the schema. This is a fairly simple task, and yet with explicit error handling (and without using the try operator, which is Rust’s answer to thrown exceptions) it looks like this. Happy path code is mixed with error handling code, and as such it can be difficult to verify the correctness of either when reading the code.
If you want to argue that the improved happy-path-readability of code which uses thrown exceptions is not worth it, I can get back to you once I finish convincing people that vim is obviously better than emacs.