I don't understand this, returning error codes has the same effect. In fact you can do that with most forms of error handling, short of segfaulting?
I don't understand this, returning error codes has the same effect. In fact you can do that with most forms of error handling, short of segfaulting?
At the moment, I'm mildly intrigued by Rusts result types which force you from the start to handle "all cases". They strongly remind me of checked exceptions.
Many languages support multiple return values, algebraic data types, or box types, allowing you to return an error value out of band.
And if the study is to be believed, checked exception are almost always dropped on the floor, just with more boilerplate and ceremony.
One benefit of not throwing an exception is that you can maintain referential transparency, which exceptions generally break. You can't substitute a function call with its value because throwing an exception will have different effects depending on the context, which consequently hinders the composability of such functions.
Exceptions are also generally not type-safe, e.g. the return type of a function tells you nothing about what exceptions it may throw. If you go the checked exception route like Java, you do get some degree of type safety, but you do so at the expense of higher-order functions, which can't reasonably be expected to know about the specific exception types its arguments may throw.
On the other hand, ADTs are composable. They work for functions that aren't defined for some inputs (maybe an input type that can't be constrained by the type system). They avoid bugs because they force the caller to deal with the exceptional case (unlike returning null, a sentinel value, or throwing a RuntimeException), but without the boilerplate of exceptions, particularly in languages with pattern matching. The caller gets to decide when, if, and how to handle the exceptional case.
Exception, on other hand, just happen and get caught in a few places.