5
$\begingroup$

I know that Exceptions as a means of flow-control is generally frowned upon.

But in my opinion, Exceptions have little value short of the flow-control aspect - after all, if you didn't want the program to continue, you could just output an error-message an terminate the program.

Exceptions, on the other hand, provide a means of reporting errors "locally", allowing a service/component to fail, and a consumer to handle the failure - and regardless of how you look at that, it is a means of controlling the flow of the program.

So here's my question - over the years, I have frequently wondered, why isn't it possible to resume execution after an exception is thrown?

Now, you wouldn't want to allow any consumer to resume after an Exception thrown by any other component, as that component was probably not designed to resume after a throw-statement, which would lead to unpredictable results.

So let's say there's a supertype of Exception called Interrupt, that allows this behavior. An Interrupt would behave just like an Exception in every respect, except that by throwing an Interrupt, you indicate that the component is ready and able to resume execution after the throw-statement, and that the stack needs to be preserved either until (A) the Interrupt has been handled, or (B) the program exits with an error-message.

Let's say we add a new "resume" statement to the language, to be used inside a traditional "catch" block - if you catch an Interrupt, and issue a "resume" statement, control would return to the point from where the Interrupt was originally thrown, and execution would continue from the next statement.

I've presented this idea in other circles, and I'm met with a lot of resistance, but no clear argument as to why this is not a good idea.

To me, it seems like a natural extension of the idea of exceptions - there are plenty of cases where this could be useful, for example while enumerating a sequence, e.g. in a function that "yields" one result at a time; an unexpected condition could occur while producing one of these results, and you may want the calling code to decide whether or not it makes sense to continue producing more results.

An exception does not allow for that.

Let's say this function throws an interrupt instead - if so, the calling code now has a chance to look at that and decide whether to resume execution (as if the exception never occurred) and produce more results, perhaps log the condition and then resume, or perhaps throw an exception, or perhaps re-throw the interrupt in case it can be handled up-stream.

I'm sure I'm not the first person to have this idea, but I would like to understand why this isn't feasible or why it's not a good idea.

(PS: I'm a programmer, not a scientist, so go easy on me.)

$\endgroup$
  • 3
    $\begingroup$ I'm inclined to disagree with the premise of your question. Exceptions are a means of control-flow manipulation, that's the whole point of exceptions. More precisely, they are a form of non-local 'jump' where the target of the jump (the place the program jumps to) is dynamically bound. In some contexts (e.g. Java or C++) it's considered bad style to use this non-local jumping for anything other than handling of error conditions that are not locally resolvable. But that's mostly because in these contexts exceptions are slow, not because of some fundamental restriction of exceptions. $\endgroup$ – Martin Berger Apr 6 '12 at 14:40
  • 5
    $\begingroup$ @MartinBerger It seems to me that this should be migrated to cs.SE - thoughts ? $\endgroup$ – Suresh Venkat Apr 6 '12 at 15:26
  • 3
    $\begingroup$ @Suresh. I have no objections, given the original question, although there are some interesting theoretical questions about resumable exceptions. $\endgroup$ – Martin Berger Apr 6 '12 at 16:08
  • 4
    $\begingroup$ @SureshVenkat The question is a bit rambling and could use some tightening up (mindplay.dk: this reads more like a position statement, i.e. a rant, than a question), but there is a real question in here, asking for an explanation of resumable exceptions and call/cc. I say migrate away. $\endgroup$ – Gilles 'SO- stop being evil' Apr 6 '12 at 23:32
  • 1
    $\begingroup$ I'm getting a bit tired of the systematic way questions get closed here. I subscribe to the syndication stream of some tags on cstheory.stackexchange, but when I come to see a question, expecting maybe a discussion about the question, 9 times out of 10 there is instead a discussion about why this question is inappropriate and should be moved elsewhere. $\endgroup$ – gasche Apr 7 '12 at 8:52
17
$\begingroup$

"Resumable exceptions" are indeed a well-known idea in some programming language circles. In particular, Common Lisp has had resumable exceptions for a long time (so they are really not confined to research language; Common Lisp could be considered "mainstream", it has been widely available and relatively widely used for a long time).

For a discussion of resumable (or "restartable") exceptions, see for example this blog post by Manuel Simoni, which links to this dylan mailing-list post by Chris Double and the following Lambda-the-Ultimate discussion: Common Lisp exception handling.

You made the insightful guess, that resumable exceptions are not a good idea in all contexts. They can make resource handling harder and more generally anything related to side-effects must be considered very carefully when you add new control-flow entry points to a block of code.

It is possible for the exception raising code to decide on whether it should be resumable or not. This is natural if you see resumable exceptions as a combination of usual exceptions and continuations: you raise an exception and, in the attached data, decide whether or not to include a continuation to the rest of the computation; the handler then decide whether or not to invoke that continuation, that is "resume" the exception.

Effect handlers (try..catch block, using-like resource handler assuming lexical lifetime) can get confused if you re-enter the computation. Combining handlers and rich control flow is difficult and gave way to a lot of different approaches in the Lisp/Scheme communities (keywords: dynamic-wind, unwind-protect). You are certainly adding non-trivial complexity to the language.

I speak of "effect handlers" because, once you have such rich forms of control flow, you get very close to the ability of presenting arbitrary effects (exceptions, backtracking, but also logging, mutable state and input/output) on top of it. See for example Andrej Bauer and Matija Pretnar's work on Eff a language based on such "effect handlers".

$\endgroup$
  • $\begingroup$ Thank you so much for elaborating on this. This board needs more people like you. $\endgroup$ – mindplay.dk Apr 7 '12 at 14:00
  • $\begingroup$ So, the handler than has to fix the state so that the program can resume from where it broke. That seems like a tall order. Are there good examples of such handlers? $\endgroup$ – Uday Reddy Apr 7 '12 at 22:30
  • $\begingroup$ @Uday no, I don't think that's expected, perhaps not even possible? that's why I proposed that interrupts be a subtype of exceptions, and that only interrupts can be resumed. The handler has to decide whether it's safe to resume or not, and the throwing code should throw an interrupt (as opposed to an exception) only if it's own internal state is still valid and it is able to continue. If you could resume after just any exception, the results would be entirely unpredictable. $\endgroup$ – mindplay.dk Apr 7 '12 at 23:55
  • $\begingroup$ @Uday the common example is interactive program development; you have a running system that suddenly gets in an error state for some reason (probably a bug), and the exception handler is the debugger, a kind of REPL that has information about where in the program the error occurred and the possibility to resume the program at this point, or at the start of any enclosing function call. So you look at the code, look at the variable states, makes a few change, decide from where it's safe to continue, and voilà, your app continues running without having to be restarted. $\endgroup$ – gasche Apr 8 '12 at 7:39
  • 1
    $\begingroup$ A better link to Eff is math.andrej.com/eff. I think we need some video tutorials about Eff, don't you? $\endgroup$ – Andrej Bauer Apr 11 '12 at 16:59
4
$\begingroup$

Regarding gasche's answer - it's true that "you are certainly adding non-trivial complexity to the language", but it's important to keep in mind that in Common Lisp and related languages, conditions are orthogonal to control flow. The core of condition handling is merely a pattern for looking up handler functions, and does not in any way alter existing or add new control flow powers to the language. See this sketch of a "condition system" in JS:

https://groups.google.com/forum/?fromgroups#!msg/ll-next/mlixSOPwc-c/iQ-xPd-kIjEJ

$\endgroup$
  • $\begingroup$ I am really glad to see this clarification. So, the Common Lisp condition system decouples exception throwing (meaning, nonlocal-exits) from condition handling. It seems wrong to characterize it as "resumable exceptions". $\endgroup$ – Uday Reddy Apr 12 '12 at 21:57
  • $\begingroup$ Right. In CL you have primitives for nonlocal exits (confusingly called throw and catch), and then you have the orthogonal condition system of signal and handler-bind. $\endgroup$ – Manuel Simoni Apr 13 '12 at 10:32
  • $\begingroup$ handler-case which comes closest to Java's try/catch is actually a combination of the two systems (see the Notes section; block, return-from, and tagbody are variations on throw and catch) -- making the point that Java and similar languages conflate condition handling and control. $\endgroup$ – Manuel Simoni Apr 13 '12 at 10:34
0
$\begingroup$

e.g. in a function that "yields" one result at a time; an unexpected condition could occur while producing one of these results, and you may want the calling code to decide whether or not it makes sense to continue producing more results

I think you are describing coroutines a la http://wla.berkeley.edu/~cs61a/fa11/lectures/streams.html#coroutines

$\endgroup$
  • 1
    $\begingroup$ Could you elaborate on your answer and why you think that this is the case? $\endgroup$ – chazisop Jun 3 '15 at 4:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.