I have always heard that "Exceptions" should be "exceptional". Meaning that most expected errors (eg: getting a non 200 response to an HTTP call, having a file not found when opening one, ...) should not be handled in exceptions, but in regular if...else blocks instead.
Exceptions are great for exceptional stuff we really could not have expected (or just don't want to deal with so we want to cleanup nicely), but they tend to be overused for "anything that is not the expected result.
On the other hand, deferring non expected path to later using exception like you clearly state sometimes actually is good thing as it speeds up development time significantly. Depending on the project, you may actually really not care, and having that power in your hand in invaluable.
I agree with this. Error handling code and exceptions are not mutually exclusive, and both have their benefits and drawbacks. Checking return codes in actually exceptional circumstances makes the code base almost unreadable, and exceptions work amazingly for this. On the other hand, using exceptions for common errors is both inefficient and ugly.
As an example of useful exceptions: malloc errors. In C, handling malloc failures is a nightmare. So much so that very few programs actually do it properly. The reason for that is that not only do you need to check the return code of malloc, you need to check the return code of every single function in your entire program that ever calls a malloc anywhere down the line.
So while this piece of code might be nice if you see it in one or two places:
int rc = fx ();
if (rc != 0) {
handle_error ();
}
Properly handling malloc errors means every single function call becomes a four line monstrosity, basically blowing up your code base by factor four and making the code much more difficult to read. Not to mention how insanely error prone it is to have to check the return code of every single function. When doing the wrong thing is so much easier than doing the correct thing, most people will do the wrong thing.
This is an API design issue. The proper solution is to provide two versions of malloc or equivalent - one that doesn't have any error code, and simply panics on failure to allocate, and another that provides a way to recover. A typical app would then mostly use the first version, and very occasionally the second when it anticipates that allocation might be so large that it could fail even in a healthy environment (e.g. loading an entire file into memory).
Panicing is not handling anything, it is just crashing the program. Unless you can catch the panic, in which case it's just another name for an exception. Not handling failed allocations except for on large allocations is just asking for rare crashes when one of the "unlikely to fail" allocations fails. Neither of these solve the problem in a robust way. Handling this properly is a language design issue. This is just applying a band-aid.
This is just recognizing the status quo. Pretty much no desktop or mobile or web software is handling "unlikely to fail" allocations. And how exactly do you expect them to handle it? If, say, it's a desktop app that's rendering a widget, and as part of that it needs to allocate a string, and that fails - how does it recover?
Panic on OOM is perfectly reasonable for most.
And yes, panics shouldn't be catchable. That's the whole point.
If you use a language designed from the start not to need exceptions, this problem is solved: The code in fact looks very much like code that uses checked exceptions (in fact, another commenter even mentioned Rust’s type system is isomorphic to checked exceptions).
I have no problem with checked exceptions. Unchecked exceptions is another story, and forces you to “catch all” everywhere (most libraries don’t document every single possible exception of every single method).
In retrospect I don’t even know why everyone responded to my original post about focusing on software error handling as if I was attacking exceptions. I think unchecked exceptions and unchecked null pointers are bad, but that’s about it; and that’s not even what my top post was about.
I would rather get rid of the term "exception" at all, and instead talk of recoverable errors (which should be properly reflected in the type system, as in e.g. Rust), and contract violations (which should result in an immediate panic).
> On the other hand, deferring non expected path to later using exception like you clearly state sometimes actually is good thing as it speeds up development time significantly.
Personally, I like the idea of Java's "checked" exceptions, particularly when your software involves lots of layers that need to use one-another appropriately, including adapting to error situations.
If you go "quick and dirty" with unchecked exceptions, you have the option to change them to checked ones later. When you do that a lot of stuff will "break", but really that will just be the compiler walking you through all the paths the exception could take, asking you to make a decision at each spot about what you want to do. (Pass the exception up the stack unchanged, catch it and wrap it and throw the wrapper, catch and recover, catch and ignore, etc.)
My argument: 'Exceptions should be exceptional', because exceptions are really just errors, that happen to be exceptional (rare)!
The reason exceptions tend to be overused is because the line between error and exception is blurry -- and it's blurry precisely because an 'exception' is really just a subset of errors. Unfortunately, most languages do not treat exceptions as a subset of an error, but as a completely disjoint/orthogonal thing, and that's the problem!
If we transitioned to languages that handle these concepts in a unified way (and ones that don't allow unchecked exceptions), this isn't a problem at all, and we can all happily write much more inherently reliable software.
Exceptions are great for exceptional stuff we really could not have expected (or just don't want to deal with so we want to cleanup nicely), but they tend to be overused for "anything that is not the expected result.
On the other hand, deferring non expected path to later using exception like you clearly state sometimes actually is good thing as it speeds up development time significantly. Depending on the project, you may actually really not care, and having that power in your hand in invaluable.