3 ms·
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 langua
by Chris_Newton 5y ago
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.
- cies 5y ago> Take Haskell, for example Yes! > 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.