|
I really, really dislike exceptions. Unchecked exceptions don't tell the caller something might go wrong. Fine for Python, where strong guarantees aren't a thing anyway, but any statically typed language cannot be content with essentially adding bottom to every single type. Checked exceptions have failed, or at least I haven't seen anybody fix their issues. They proliferate spurious exception types in interfaces. They are inflexible, as they usually can't be generic. They suck at typing error cases for higher order functions. They're big heavy and expensive, so can't be used for hot code paths. They're exceptions but more often than not you want to signal expected failure... The list goes on... |
Failure modes are an abstraction violation; they're a function of implementation. That's what makes checked exceptions not work, at the end of the day. Information-carrying exceptions reveal implementation details. So a module author must decide between hiding details and wrapping everything up in module-specific exceptions that user code can't actually use to make decisions most of the time, or expose implementation details that turn into a versioning problem over time.
There's roughly two situations for error handling: near the leaf of the call tree, where you have enough context to deal with an error, and need to switch on error type and take compensating action; or near the root of the call tree, in the main loop, where you log errors and terminate requests etc. in a generic way (e.g. 500 response).
Exceptions aren't ideal for the first situation but are great for the second. Error codes are adequate for the first situation - monadic error types (Result<T>, Either) are a bit better - but suck horribly for the second, because you need to manually unwind, writing boilerplate that should be automatic.
And at the limit, error types are isomorphic to checked exceptions, with the same problems, and more - error types introduce an aggregation problem, where multiple errors need to be joined together. You can still get that with exceptions too but it usually requires parallelism.