5 ms·
I basically agree with you. I think return codes are simpler, and exceptions appear simpler because we're used to them. In reality they are a magical, out-of-ba
by wonderzombie 14y ago
I basically agree with you. I think return codes are simpler, and exceptions appear simpler because we're used to them. In reality they are a magical, out-of-band mechanism compared to plain old return. I contend that local and explicit is more strongly correlated with simplicity than remote and implicit.
Put another way, verbose code can be simpler if it is direct and explicit — what happens is what's on the page — just as succinct code can be more complex if it is magical or implicit — what actually happens depends on who is calling it, say.
- slurgfest 14y agoI used return codes before I ever used exceptions, and even chafed at exceptions. Exceptions CAN be simpler. It's just an expressive tool, it matters more what you do with it. I do hate to read code which uses exceptions as a normal part of program flow, it's very GOTO-like and remote and implicit. But I don't write code that way and nothing is forcing me to write code that way. When exceptions are reserved for really exceptional conditions where a requested performance CANNOT continue, they are used much less frequently, and more locally and explicitly. In short I think the problem is a matter of philosophy more than language facilities, and the big differences are within-language rather than between-language. The reason I like to have exceptions used as a convention is that I think a better default for programs which have not yet covered some corner case is for them to decline to run, rather than to run in dishonor.
- mercurial 14y agoSay you need to migrate a database to a new schema. You need 10 SQL statements to do this. Each statement may fail, in which case you want to write stuff in a log and abort the transaction. In this case, wouldn't you agree that writing your statements in a try/catch/finally block would result in clean, easy to understand code, as opposed to adding explicit error handling after each statement?
- acqq 14y agoI wouldn't agree, as when I'd code it in C I'd have a function callSql which returns false and write the code you describe as: if ( callSql( s1 ) && callSql( s2 ) && callSql( s3 ) && callSql( s4 ) && callSql( s5 ) && callSql( s6 ) && callSql( s7 ) && callSql( s8 ) && callSql( s9 ) && callSql( s10 ) ) { endTransaction(); return true; } logAndAbortTransaction(); return false; No need for exceptions at all, and it's very clean and clear what's going on. I wrote the same code to my young exceptions-indoctrinated colleague who asked the same thing like you, and he couldn't believe at the start that that's all -- that much he wasn't used to a plain control flow. It's even obvious how it can be generalized for any sequence of commands: for ( int i = 0; i < n; i++ ) { if ( !callSql( s[ i ] ) { logAndAbortTransaction(); return false; } } endTransaction(); return true;
- dagw 14y agoWhile this code is nice and concise, the problem is that you don't automatically know which call failed or what the error was.
- acqq 14y agoIf you can, write a more concise code with exceptions which will automatically show you which call failed and what the error was. If we include all the declarations, I claim that your solution wouldn't be shorter or more readable than my C variant I'll write, which would only declare error code values, or even return strings as error reports. There's no advantage in exceptions, except when you use them for "hardware-like" generated exceptions (which is where they belong).
- mercurial 14y agoThat's very easy: void migrate() { try { callSql(s1); callSql(s2); ... endTransaction(); } catch(SQLException ex) { logAndAbortTransaction(ex); } } Now I have an exception to log, which means I have a stack trace which is going to tell me which statement failed rather having to play the guessing game. I may even get an explanatory message for free if the exception came with a message. For me, having exceptions over return codes has a number of advantages: * Within a single object, you get much more information (error message and stack trace). You need to return two different values if you need both a message and a return code * You don't want to handle the exception at the call site? no problem, just propagate it up the stack. On the other hand, if you want to try the same thing with return codes, you need a way to differentiate the return codes due an error in your migrate() function and the error codes resulting from a given callSql statement. * Which brings us to... another benefit: enforced compile-time checking of the exception. You can't write code that is going to check for a DogException when the callee throws a CatException. But it's very easy to check for -1 when the actual code is -2. Of course you can alleviate this somewhat using static constants, but it's still a safety issue. Obviously, exceptions are not all roses either, whenever you need a new one, you need to create a class for it. But the workflow interruption is not greater by an order of magnitude than adding a static constant somewhere to represent your new error code. Edited for better formatting.