2 ms·
Preferences for exceptions vs error codes is usually determined by the desire (or not) to have a control flow edge specific to handling the error case, which in
by barrkel 8y ago
Preferences for exceptions vs error codes is usually determined by the desire (or not) to have a control flow edge specific to handling the error case, which in turn depends on what kind of code the programmer is writing.
Exceptions are optimized for unwinding (i.e. abort, not handling); error values are optimized for handling; while monadic Maybe / Either constructions try to have it both ways, and are approximately equivalent to checked exceptions with more cumbersome syntax, depending on how good the language is with lambdas. The heavyweight syntax reminds you of the error's existence.
The thing is, while some errors conditions can easily be put in the exceptional category (panic / abort), and others can easily be put in the "you really should handle this, it's not unusual" category, there's a large space in the middle where the preferred choice is heavily dependent on context and the program being written.
Business code called from a server loop almost never cares about exceptions; unwind all the way to the top, rolling back transactions along the way, log, and loop back around to handle the next request. Networking code in a client or server almost always wants to handle errors from the network; network errors are not unusual.
It's almost like the choice should be up to the user at the call site. Unfortunately the decision needs to be global, because error propagate. Errors are peculiar like this: they violate abstraction boundaries. The failure modes of a module are a function of how it's implemented. If the module interface is designed to be polymorphic, wrapping up error conditions in abstract values makes them almost unhandleable - hiding the cause means you can't take action. Thus errors are a leading cause of leaky abstractions.
Erlang was not originally designed for concurrency; it was designed for fault tolerance. That resilience was achieved not by handling errors, but by tearing down the faulting process, and letting a supervisor set things back up again. Killing the process also means throwing away all the data it had in flight, and because data isn't shared, the risk of corruption is much lower, and semantically the tear down looks a lot like a transaction rolled back.
Erlang appears to be pretty good at achieving its goal of fault tolerance. Does it look more like error codes or exception handling? A lot more like exception handling if you ask me. But is all the rest of it needed to follow through properly?