Jonathan Scott Duff wrote: > > I have only a passing familiarity with exception handling and have > only used it in "toy" programs. I'm just trying to wrap my mind > around the whys and wherefors. I understand how it works, but not > why it works the way it does, any references to exception handling > would be much appreciated. I'll take a stab at the whys and wherefors here, in an attempt to help elucidate the terminology and functionality we have been persuing... A throw is a kind of non-local goto implemented not with an instuction counter write, but with stack unwinding, in order to make sure the non-local goto doesn't corrupt the stack. This is pretty much required in a stack-based architecture, because the subroutine call/return sequence depends on stack containment. An exception is like a "tag" that keeps track of data about the non-local goto while the stack is being unwound. A try + catch is like a statement label; it is a target for the goto, but it is a dynamic target match, it is based on the exception data given to the throw, and it happens while stack unwinding. Of course, during the "dynamic" part of label matching and handling (during stack unwinding), you must also arrange to handle additional non-local gotos. That's why it's hard to code this common usage with only Perl 5's eval {}, die, and $@, and that's what things like try, catch, and finally are for. For example, say you want to write (using RFC 88 terminology) something like this: try { may throw } # eval with catch/finally stuff catch { may throw } # only if exception raised finally { may throw }; # whether or not exception raised # including exception from catch! # If we get to here try and finally didn't throw, or try # threw but catch didn't throw and finally didn't throw. With Perl 5, you have to say something like this: eval { may die }; # try my $unwinding = $@; if ($unwinding) { eval { may die }; # catch $unwinding = $@; } eval { may die }; # finally ($unwinding ||= $@) and die $unwinding; It gets even more complicated when you want conditional catches where the computation of the predicate may throw, and it gets much more complicated should you actually want access to an exception stack while unwinding. And the purpose of the code quickly becomes quite unclear if each of the "may throw"s gets replaced by a half- dozen lines of code. Those of us who are working on this problem are looking for some sort of infrastructure to help us out. Exception *handling* (Perl 5's eval and die) is not the same thing as exception *objects* (Perl 5's $@), although the two are commonly used together to implement what might be called "structured error control" (and other things too, one must remember). See RFC 88 for more info: it provides some simple and some not-so-simple examples. The basic idea behind using exception handling and exception objects to look after *error* handling (where error more or less means "assertion failure") is that is a pain to have to check every function call for a failure return code, especially when most of the time you would just want to return your own failure code anyway. So instead of using return codes, functions throw to indicate failure, and some sort of catch mechanism is explicitly provided to handle cases where you actually don't want to just return your own failure. This technique turns out to fail more "gracefully" too, because if you forget to check for failure you get failure (including program termination if no catch gets in the way), not continuation with possibly bogus state. One of the problems with this approach is that once people start to do it, all programs must accept the defaults or otherwise agree to play along. Given eval {}, die, and $@, I think we're pretty much past that point now. All in all, it turns out that once you allow this non-local stack- unwinding goto thingy (aka die, or throw), you end up needing functionality like try (or eval), catch (do block if unwinding), and finally (do block whether or not unwinding) to conveniently do the things you're going to want to do with it (like error handling, for example, and handling exceptions while handling exceptions). Then, before you know it, you are going to want to make catching conditional on the current exception, or maybe current exception stack, and you're going to want all kinds of debugging information (which you can only get from the *throw*, not from the *catch*, because then it's too late). You're also going to find yourself thinking about what a standard exception should be, and what error exceptions should be, and why the two aren't necessarily the same. And then, trying to deal with all this, you're going to end up having a discussion like this ;-) Yours, &c, Tony Olekshy