5 ms·
>I don't get that. Call/Return Pairs are balanced at runtime, not lexically. From all possible returns in a function, only a single one runs, and terminates th
by Banana699 4y ago
>I don't get that.
Call/Return Pairs are balanced at runtime, not lexically. From all possible returns in a function, only a single one runs, and terminates the whole function. Every call site is paired, at runtime, with a single return that will return control to it, and the entire set of candidate returns are known at compile time, and the compiler checks all of this to report dumb mistakes.
This is not how Exceptions work, you can throw more than you can catch, and you can catch more than what can ever be thrown (this is generally harmless, just garbage code that never runs). Neither the number nor the types of throw/catch sites are kept in sync statically.
>a return can return to multiple different places depending on where the function was called from
Every single call site of a function is paired with all the returns of this function. The returns of a different function are paired with the call sites of that function, not the first. Unlike Exceptions, where every catch is a jump destination for every single throw of the same (dynamic) type across every single function beneath the catch on the call stack. That's literally an exponentially larger set than set of all the returns of a single function.
>why that would even be a desirable property
It's desirable because it gives static gaurantees.
Given a throw, you can't even know how much of the stack it would unwind, it can potentially unwind the entire stack if no compatible catch is above it on the call stack*. Exceptions can break through their abstraction boundaries and unwind the stack of a calling component that doesn't know about them. Exceptions are fundamentally dynamically typed Returns that you can forget about. That's as big a footgun as you can get without being deliberately a troll.
Given a catch, you can't even know where is all the places that can go to that catch (unlike a call site). Oh, there is the trivial answer of course : Every single function called in the try{...}, and every single function that those functions call, etc etc etc. Again, an intractable thing to reason about. So people don't, but they have to, the throw/catch pair is a single logical feature, their uses must be reasoned about and kept in sync in assumptions and consequences.
None of this is how a return works. If Structured Programming advocates saw Exceptions, they would be horrified. Even C's gotos don't allow you to jump out of a function.
* : (which, of course, is a dynamic property: in "if(...) then {Do_Something() catch(){...}} else{...}", only one branch will actually catch, one branch can crash the program, and none of this is visible to the compiler at write time)
- wizofaus 4y ago"If Structured Programming advocates saw Exceptions being used for regular logical flow, they would be horrified". Agreed. Unfortunately Java and in some cases C# tend to encourage this because of the behaviour of library functions, e.g. those that parse numbers and/or dates (a string not being in a parseable format rarely justifies being considered an exception, at least at a library level). On the other hand as an advocate of structured programming I find code that's full of ifs and multiple return statements and global error state variables to handle truly exceptional conditions where code is unable to carry out its primary function to be horrifying.
- Banana699 4y agoThere is no distinction between "regular" and "error" control flow when it comes to reasoning and understanding, they are both control flow, they both need to be planned and synchronized in the programmer's head. The arguments I give is for why Throw/Catch is a terrible control flow construct, a throw is an extremely dynamic goto that searches the call stack for its label, not guaranteed to be there by the way, and a catch is an extremely dynamic call that can resume control from any point underneath in the call stack. I don't know about you, but sounds to me like a disaster waiting to happen. Java tried to tame it by trying to incorporate exceptions into the type system, but it's half-baked and also comes with a big glaring hole called Unchecked Exceptions. Errors or no errors, a bad control flow construct is a bad control flow construct. Error control flow deserve the same treatment as regular one. >I find code that's full of ifs and multiple return statements and global error state variables to handle truly exceptional conditions Two wrongs don't make a right. Ad-hoc error handling is awful, but so is Ad-hoc exception handling as well. There is plenty of error handling mechanism (either built-in to languages or as an architecture over boring code) that is not both.
- wizofaus 4y agoCode that I've worked with over the last 25 years not using exceptions almost invariably has worse error handling than that without. YMMV. (As far as regular vs exceptional flow goes, think about what sort of acceptance criteria are written into story requirements. I've never seen the expected behaviour for out of memory or even critical network failures written up as part of a user story. In many cases it's decided for you by the libraries used anyway)