try {
someCall();
} catch (ErrorICanHandle err) {
//handle
}
perfectly equivalent to this: err = someCall();
if canHandle(err) {
//do something
} else {
return err
}
I don't see why so many people think exceptions make code harder to analyze. In my opinion, it is errors themselves that make code hard to analyze, regardless of implementation strategy.The only difference between exceptions and error returns/error codes is in human readability: one makes the bubbling behavior explicit, which clutters the code but makes it obvious; the other makes the bubbling behavior implicit, which keeps the code cleaner, but also less obvious.
After writing professionally in both languages with Exceptions (Java/C#/Python) and in Go, I much prefer Exceptions to error values/codes, but I can appreciate that it may be different in other domains or for other people.
Yes, this is exactly what I meant.
> it is at least explicit in what functions can fail and which don't
So is Java with exceptions (at least as long as developers are even slightly disciplined).
try {
someCall();
someOtherCall();
} catch (ErrorType err) {
//handle
}
Which method throws? If they both throw ErrorType but one of them cannot be handled, then you have to put additional logic into the catch and rethrow.This is a common source of faulty error handling.
try {
someCall()
} catch (ErrorType err) {
//handle one way
}
try {
someOtherCall()
} catch (ErrorType err) {
//handle another way
}
It's no less verbose than the error values way, though again, it is easier to make the mistake in the first place.I'm not trying to claim that exceptions are ultimately better than error values, just that the difference isn't so much "non-local control flow" as it is explicitness vs implicitness. Exceptions are implicit, error codes are explicit. Both have benefits and drawbacks.
someCall()
is equivalent to code in a language without exceptions that looks like this: err = someCall()
if err {
return err
}
(ignoring for a moment non-error returns, which don't change the point significantly)Having functions that are guaranteed not to throw / return errors doesn't significantly change this shape.
So basically it's no more non-local than the code you'll end up writing with error codes, in most cases, any way. The only difference is implicit vs explicit.
> `ErrorICanHandle` may be a kind of exception that the `someCall` function has no idea about whatsoever -- it could be an exception propagated from much farther down the call stack -- while your if statement assumes the error was explicitly returned from the function.
someCall doesn't need to know about the meaning of the error in either case. It only needs to propagate any error it receives from any functions it calls (that it can't handle itself). Think about the function I wrote myself: did it need to know the precise type of err? Nope - it just needed to know that it implements some kind of Error interface (which may be as simple as being a negative number, like a POSIX error code).
Edit to give a more complete example:
extern void bar();
void foo() {
bar()
doOtherStuff()
}
void someCall() {
foo()
doMoreStuff()
}
void main() {
try {
someCall()
doFinalStuff()
} catch (BarSpecificException e) {
doErrorStuff()
}
}
Is equivalent to this code: extern err bar()
err foo() {
err = bar()
if err {
return err
}
err = doOtherStuff()
if err {
return err
}
}
void someCall() {
err = foo()
if err {
return err
}
err = doMoreStuff()
if err {
return err
}
}
err main() {
err = someCall()
if err is BarSpecificException {
err = doErrorStuff()
if err {
return err
}
} else if err {
return err
}
err = doFinalStuff()
if err is BarSpecificException {
err = doErrorStuff()
if err {
return err
}
} else if err {
return err
}
}
If I ask you in the second piece of code what will be executed after the call to foo(), is it any easier to reply than in the first version? Personally, I don't believe so.The bigger difference is knowing whether you should expect errors at all - with exceptions, you can forget to handle an error and you may screw up an important assumption, like not unlocking a Mutex if an Exception is raised. With error codes, you have the opposite problem: you may forget to check for an error, and continue to execute in a bad state.
Nitpick: I'd say if you need the compiler to optimize exception handling you are using them wrong.
Exceptions are for exceptional circumstances.
(if you mean that exceptions mess up optimization of surrounding code that could be a bigger deal but I won't accept that without pointers to benchmarks$