| Posted by H. S. Teoh in reply to meta | PermalinkReply |
|
H. S. Teoh
| On Thu, Feb 24, 2022 at 11:59:56PM +0000, meta via Digitalmars-d wrote: [...]
> Nobody said to verbatim copy what C does, I personally explicitly said to take that idea and evolve it, just like the newer modern languages are properly doing (Go, Zig, Rust, Odin)
Go, Zig, Rust, and Odin all require explicit checking for error returns. This makes them suffer from exactly the same problem as my C example: after every single function call you must explicitly check for error conditions. It doesn't matter what the implementation mechanism is, whether it's an int return, or a sum type, or a discriminated union, or a second return value. The point is that you have to check this error code *explicitly*, on *every single function call*.
This kind of error handling approach is fundamentally flawed because it incentivizes programmers to do the absolute minimum they can to shove the error under the carpet so that they can get on with making progress in the problem domain. Like ignore the error return value if they can, write generic "if error ignore it" boilerplate around their code. Or just suffer from .unwrap hell. It clutters code with error-handling paraphrenalia, making it needlessly verbose, and less composable.
For example, if you have function f that calls g and h, and g and h have their own set of possible error enums, then what should f return? You either create yet another enum that subsumes both (and the caller then has the hairy task of making sense of which error codes comes from where and what to do with it), or, if you have to do this for all 65,535 functions in your entire codebase, just give up and go to a global binary {ok, fail} enum. Or, to harken back to my C example, OK and INTERNAL_ERR, which is where all return-code-based error handling inevitably gravitates towards. Which is about as useful as a BSOD every time the program encounters the slightest difficulty.
A thrown exception allows most code to eliminate all error-handling paraphrenalia, making code more readable and maintainable, and at the same time any actual errors are not ignored by default, you have to handle them somewhere up the call stack. And the exception contains specific information about the problem, not just the equivalent of INTERNAL_ERR (or worse, that stinky antipattern of storing an error message in a global variable that you then have to call some API function to extract).
> Tagged Unions, Multiple return type, enum/type inference, constructs to bubble up things etc.. we must move forward!
I've yet to see a convincing example of error handling based on tagged unions / multiple returns / etc., that doesn't suffer from the explicit handling problem I describe above. If you have one, please enlighten me.
> I suspect in the near future, exception handling will be seen as very bad practice, if not already, we must prepare..
I've always been a skeptic of reacting to hypothetical futures that have not be proven is inevitable.
> We no longer live in an era with single cores!
Nobody has yet answered my initial question about whether D's exception throwing mechanism holds a global lock, which is the primary issue identified in the OP's linked article. Until this question is answered, all this talk about exceptions being bad for multi cores is just beating a strawman. It's tantamount to saying, since the global lock in C++ exceptions causes performance problems, exceptions must therefore be bad (even when no global lock exists). It's a non sequitur. The problem observed in the article is caused by the global lock, not by exceptions per se. (And whether exceptions in and of themselves necessitate a global lock has not yet been established.)
T
--
May you live all the days of your life. -- Jonathan Swift
|