> The list of recoverable exceptions that can be thrown by a method should be part of the contract.
If it’s really recoverable, then should it actually be an exception? How often do you see exceptions that are recoverable?
> If not, then to avoid crashing you would have to catch the root Exception class, which everyone agrees is a bad idea.
I disagree that it’s a bad idea, having a catch-all exception handler to avoid crashing the entire app is a good idea.
Java also has RuntimeException, and with it, a whole host of exceptions that may occur but are not declared. Should every function that uses the division operator be marked with `throws ArithmeticException` since it may end up being a division by zero? That would quickly become absurd.
If it’s really recoverable, then should it actually be an exception?
That's the problem with exceptions in general. They're a hammer that makes everything look like a nail. People start using them for all kinds of control flow situations because they're more convenient than having to deal with a lack of type system support for optional values, etc.
You get to the point where you have a parser that is expecting a digit and it encounters an alphabetic character so it throws an exception!
There are remarkably few situations involving recoverable errors, though, and they almost all look like "not found"--which includes your parse integer example--and, even then, most of the time you will want a forceful automatic exception variant and not a "recoverable" error value as there is almost never anything to do instead: try-parse is so rare of a thing that is correct to do it warrants having a quick "try" attached to the expression.
In contrast, there are a billion ways in which almost every line of code can fail--even stuff people are super sure could never fail is still usually at least subject to stack exhaustion--and almost none of these failures are things you should locally "handle"; and yet in many--even most--cases these are situations you can still recover from at a higher level if you don't screw up your own logic (which is not the point of these errors).
A parser failing is something I'd expect to be recoverable. And I'd expect it to return where it failed and why. You can do that without throwing exceptions at all, and instead encode it in the type. Like returning a result<parsedObject,failureReason>. (Can be further refined, like adding the already parsed/yet to be parsed string to the failurereason as a value) I WANT to be forced to handle it right there and then rather than potentially forgetting a try catch, or checking the boolean returned if it was successful or checking the out parameter if it is not null (boolean tryparse(out parsedObject) is something I see commonly)
For me it is much more ergonomic to use a
match tryparse somestring with
| Ok obj -> proceedFurther obj
| Error reasons -> handleerror reasons
For truly exceptional cases you don't expect to be recoverable? Yeah, throw an exception.
It may not be recoverable locally, but the caller may know how.
Consider Files.createFile. It throws if the target already exists. That method has no way of knowing what the recovery is, however, the caller might.
It could be that the caller wants to explode, but maybe its part of some kind of singleton launch where the recovery is to pass some piece of data off to whoever created the file originally.
Maybe this is a bad example since there’s not much interesting in the return of Files. But it’s exceptional in that it’s an abort rather than a success.
That said, checked exceptions are still trash because they don’t work across thread boundaries or with futures. ExecutionException is the catch all baked into the design because they needed something. The idea could have been good though because naming error handling visible is useful. Java just has the misfortune of designing early before the kinks had been worked out and now we get nice stuff like Rust has.
Recoverable, as a technical term. You can catch an NPE and follow it with any kind of code you wish, it will be correctly “handled” by the VM. (The semantics of course depend on the exact specifics, but returning a server 500 error and not failing the process for example in a web server is completely valid).
You can try catching an OutOfMemory Error, but it is not guaranteed that your handling code can successfully run. Hence, non-recoverable.
If it’s really recoverable, then should it actually be an exception? How often do you see exceptions that are recoverable?
> If not, then to avoid crashing you would have to catch the root Exception class, which everyone agrees is a bad idea.
I disagree that it’s a bad idea, having a catch-all exception handler to avoid crashing the entire app is a good idea.
Java also has RuntimeException, and with it, a whole host of exceptions that may occur but are not declared. Should every function that uses the division operator be marked with `throws ArithmeticException` since it may end up being a division by zero? That would quickly become absurd.