| I'm firmly in the camp that believes that exceptions are a false economy. The post links to an "Exception Smells" post that doesn't mention one of my pet peeves: exceptions as control flow. For example, Java's parseInt [1] throws a NumberFormatException if the string can't be parsed. IMHO this is terrible design. As a side note, checked exceptions are terrible design. I wrote C++ with Google's C++ dialect where exceptions were forbidden. Some chafed under this restriction. It was largely a product of the time (ie more than 20 years ago now when this was established). Still there's debate about whether it's even possible to write exception-safe C++ code. In the very least it's difficult. So Google C++ uses a value and error union type, open sourced as absl::StatusOr [2]. The nice thing was you couldn't ignore this. The compiler enforced it. If you really wanted to ignore it, it had to be explicit ie: foo().IgnoreError();
But here's where the author lost me: this chaining coding style he has at the end. To make it "readable" a bunch of functions had to be created. You can't step through that code with a debugger. The error messages may be incomprehensible.I much prefer Rust's or Go's version of this, which is instead imperative. [1]: https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.... [2]: https://abseil.io/docs/cpp/guides/status |
and in doing so created code that was far more self-documenting and evidently correct. the counter-example was no where as easy to reason about (imo), even for the simple example.
> You can't step through that code with a debugger.
i don't think it's fair to comment the content of the idea based on the quality of existing debuggers. in any case, i don't think it's true that you can't step through this code on a debugger (generally): VSCode & Roslyn have no issue with this sort of structure in C#.
> The error messages may be incomprehensible.
the ones in the example may be. i've worked in a large code-base using this approach before, and there was rich error information. transformations of failure states (i.e. the Error values) are easier to do with context, as opposed to your catch-block which has no knowledge of the context in which an exception was thrown.
> I much prefer Rust's or Go's version of this, which is instead imperative.
go's (err, val) "error handling" paradigm is, imo, it's worst feature. i can't talk for rust. whilst your assertion that a failure condition is impossible to reach may be true for the code you write today, it almost certainly wont be in the future..
how many error dialogues have you seen saying some variant of "unreachable state reached"? :)