> I much prefer writing and reading code with exceptions than explicit error handling control flow.
Can you let us know what languages that use "explicit error handling control flow" you have used?
I've extensive experience with both an much prefer the "explicit error handling control flow" in Rust/Haskell/Elm/Kotlin/ReScript, than the exceptions in Java/C++/C#/JS/Ruby/Python.
Interesting that the use of implicit nulls (another of my annoyances in langs) is also split along these lines!
I've extensive experience with both an much prefer the "explicit error handling control flow" in Rust/Haskell/Elm/Kotlin/ReScript
How explicit are those languages really, though?
Take Haskell, for example. It’s common to indicate potential failures by having a function return Either/Maybe. Those are monadic, with their join behaviour propagating the Left/Nothing result that conventionally represents the failure case(s). It’s idiomatic to write a function that calls a series of potentially failing functions and not examine the result of each call immediately. Instead, you defer any handling of failures to the end of the chain or even pass the result back to the calling function via another Either/Maybe.
What you’re not doing in any of those alternatives is explicitly checking the return value after each function call and handling any failure immediately right in the middle of your default execution path. So is this really much more explicit than exceptions? The possible failure modes for each function are encoded in its type, but there are implementations of exceptions that also have that property, so that’s not really a point in favour of either side. The code calling the functions is still highlighting the default execution path and shifting the recovery from any other cases elsewhere.
As another example, Rust follows an analogous convention with functions returning Option/Result to indicate potential failure modes, but has try! and then added the ? operator to propagate errors with minimal extra code instead of writing all the boilerplate manually each time. This is arguably more explicit when calling those functions, since at least any function that can fail needs to be called with ? (or something more obvious) to handle the result, but fundamentally the convention is still trying to minimise clutter in the default execution path and delegate error handling responsibilities to code somewhere else, and it still doesn’t indicate anything more explicit about what the potential failure modes for each function are at the place where that function is called.
> Instead, you defer any handling of failures to the end of the chain
That's a choice. You can also do it immediately.
Also, no warped control flow: just regular "call-return" flows.
> What you’re not doing in any of those alternatives is explicitly checking the return value after each function call and handling any failure immediately right in the middle of your default execution path.
Like said, that's a choice. You can setup monadic handling, you can do each by itself. It depends on the situation what you use: but the DEFAULT is each by itself.
> As another example, Rust
Still the control flow remains intact. While Exceptions mess with control flow.
Can you let us know what languages that use "explicit error handling control flow" you have used?
I've extensive experience with both an much prefer the "explicit error handling control flow" in Rust/Haskell/Elm/Kotlin/ReScript, than the exceptions in Java/C++/C#/JS/Ruby/Python.
Interesting that the use of implicit nulls (another of my annoyances in langs) is also split along these lines!