Comment by fluoridation
15 hours ago
>Let’s say I have a physics system that runs an update. Assume we catch outside the update. If anything throws the system is now in an intermediate state and is effectively irrecoverable.
That's a transaction kind of scenario. You catch at a recoverable point and rollback to a good state, and if that's not possible, then you simply fail out. I don't understand; what problem did exceptions introduce here? An exception was thrown (i.e. an error happened) during an intermediate operation and the operation as a whole stopped. What would have changed if every function had used error codes instead? It would have been either an error you were incapable of handling (in the sense that you didn't even look at the error code), and the operation would have silently continued in a possibly corrupted state, or it would have been an error you were capable of handling, in which case implement that same handling logic for the exception code.
if (op1() != SUCCESS){
//recover and continue
}
if (op2() != SUCCESS){
//nothing to do so just fail out
return FAIL;
}
//don't care about error so don't even bother checking
(void)op3();
//now the state of the program may be invalid
op4();
becomes
try{
op1();
}catch (/*...*/){
//recover and continue
}
op2(); //let exception be caught by someone who can do something about it
op3(); //same
//sometimes we won't get here, but at least if we do, we know the state of the program is valid
op4();
Is it just that you like ifs more than try-catches?
>If you want to argue that exceptions should only be used for irrecoverable errors such that the subsystem is not expected to resume then I would list.
That would depend on what you mean by "irrecoverable error". I interpret that to mean that the program cannot continue to function safely and it's better to terminate ASAP than to attempt to do anything else at all. If that's what you mean then no, that's not what exceptions are for. Like I said in a sibling comment (https://news.ycombinator.com/item?id=48527216), exceptions are meant to signal an error that may or may not be recoverable that the immediate caller may not know how to handle. Someone in the call stack should be able to decide based on program state how to respond to the error condition; sometimes that will involve rolling back to a valid state, as I said before, perhaps retrying; sometimes you will cancel the operation and discard the interrupted computation; sometimes you will notify the user or log an error; sometimes you will decide that there's actually nothing else for the program to do and clean up and exit.
>You tell me an example of a system that doesn’t use exceptions where adding exceptions makes it better.
Sure, no problem:
if (op1() != SUCCESS)
return FAIL;
if (op2() != SUCCESS)
return FAIL;
if (op3() != SUCCESS)
return FAIL;
//etc.
with exceptions becomes
op1();
op2();
op3();
//etc.
All else being equal, exceptions make this kind of code better by making it more readable. If the non-exception code needs to return both error codes and partial values, it becomes even more noticeable:
auto [result1, error1] = op1();
if (error1 != SUCCESS)
return FAIL;
auto [result2, error2] = op2(result1);
if (error2 != SUCCESS)
return FAIL;
auto [result3, error3] = op3(result2);
versus:
op3(op2(op1()))
> All else being equal, exceptions make this kind of code better by making it more readable.
I just fundamentally disagree that hidden secret control flow makes code more readable. Well, it may be more readable but it is, imho, significantly less understandable.
There’s a reason that literally no modern systems language has adopted C++ exceptions.
We’re just about at the point of discussing syntactic sugar. So one question is “given current C++ capabilities should you use exceptions or not?”. Another is “should C++ add different sugar for error handling?”. And a third is “should a different language adopt exceptions-like design?”.
Imho a zig or rust like error system with a ? operator to return is vastly superior. Fallibility and control flow is super explicit, obvious, and easy to read.
Current C++ is a little jankier. Designs vary. All have tradeoffs. But imho they are all preferable to secret hidden control flow. So exceptions are, in my lived experience, a strictly inferior choice.
>I just fundamentally disagree that hidden secret control flow makes code more readable. Well, it may be more readable but it is, imho, significantly less understandable.
That's cool. I disagree that the principal control path should be made more difficult to read in favor of having really explicit error handling that doesn't actually do anything. That said, I do like Rust's question mark operator; it's a pretty cool middle ground between exceptions and error codes.
So, about that example of exceptions introducing faulty behavior unrelated to resource management that I asked for?
> example of exceptions introducing faulty behavior unrelated to resource management that I asked for?
Well there’s a reason that constructors aren’t allowed to throw. Because it would leave objects in a weird hybrid state.
My physics system example would be the canonical one. You have a system that is transforming from one state to the next. An exception is thrown in the middle of the state transition. Whatever invariants you had for either state is (plausibly) not held. I believe your solution to this was to treat it as a transaction and rewind/restore to the previous state if an error is encountered.
It’s difficult because I would simply literally never use exceptions for anything ever. My experience with them is strictly negative with zero positives of any kind. I genuinely can not think of a single thing they make better. Nor have I ever encountered a library or codebase where I felt exceptions made it better. But boy howdy have I been frustrated by exceptions.
boost often has two APIs one with exceptions and one with error code reference arguments. I intend to always use the EC version. And god damn is it fucking frustrating when I accidentally use the exception version and so I get a runtime exception for a vanilla error that I wasn’t expecting. Which is why hidden control flow is evil!
4 replies →