From a type theory perspective, an exception is another type of return value that a function can have, and not documenting those in the function signature is absurd.
I have seen libraries that make network calls that do the following:
* Throw an exception on certain types of network errors
* Return an HTTP error code for other types of network errors
* Return an object with error set to true and an error string for other types of network errors
NONE of those was documented, it is crazy. This actually bite me in prod when an undocumented exception that we hadn't seen in over a year of uptime was finally thrown. I had to look at network logs to try and figure out why it was being thrown (no checked exceptions in JS!), which itself is absurd.
I think the issue is with checked exceptions as a solution to how to declare this signature, or at least with how they are designed in Java: if you add a new exception to the signature, every downstream call site needs to change, recursively.
This means it is impossible to add new exceptions to library methods without breakage, and even in your own code it may mean hundreds of changes throughout your code to add a new exception type.
Rusts solution seems to have improved this conundrum, where you can describe how to map the new error into an existing hierarchy in a single or a few places instead of at every call site
> If it wouldn’t, an exception of that kind could bubble up at a place you didn’t expect.
You should always be able to handle an unknown exception. Sure you can't do much about it, but it shouldn't be a big deal. An exception only occurs if the code can't continue as expected.
Libraries are supposed to be a point of abstraction. They should be allowed to change their implementation fundamentally as long as they continue to respect the same interface. Exceptions should not be part of the interface explicitly because are they implementation-detail related.
If you don't control the code where the superclass or interface is defined then you're stuck. You can't change that code even if it would be only a tiny edit. Checked exceptions are absolutely not a "feature".
From a type theory perspective if you want to track exceptions as return values you want a composable container class (best case, a full monad) so that you aren't writing a lot of ad hoc code every time to handle them.
That's a big problem with Java's checked exceptions, it's not very composable and flow control can just stack up into deeper and deeper "waterfalls" of code rather than simpler pattern matching. (To be fair that's a big problem with exceptions in general as "flow control", they aren't very composable.)
Something like an Either monad in a language with sum types can be a great way to describe errors/exceptions usefully as return types. From a type theory perspective, checked exceptions can be seen as a hack for a language that doesn't support sum types and doesn't have great native monad binding.
I have seen libraries that make network calls that do the following:
* Throw an exception on certain types of network errors * Return an HTTP error code for other types of network errors * Return an object with error set to true and an error string for other types of network errors
NONE of those was documented, it is crazy. This actually bite me in prod when an undocumented exception that we hadn't seen in over a year of uptime was finally thrown. I had to look at network logs to try and figure out why it was being thrown (no checked exceptions in JS!), which itself is absurd.