In Apocalypse 4, Larry Wall wrote: | | In fact, a C<CATCH> of the form: | | CATCH { | when xxx { ... } # 1st case | when yyy { ... } # 2nd case | ... # other cases, maybe a default | } | | means something vaguely like: | | BEGIN { | %MY.catcher = { | given current_exception() -> $! { | | when xxx { ... } # 1st case from above | when yyy { ... } # 2nd case from above | ... # other cases, maybe a default | | die; # rethrow $! as implicit default | } | $!.markclean; # handled cleanly, in theory | } | }
Beautiful. The synthesis of CATCH, BEGIN blocks, %MY, given, when, break, dwim =~, die, $!, $!.clean, and $!.stack is awe-inspiring. The way proto-exceptions, fail, and use fatal work together is also brilliant. I particularly enjoyed this one: CATCH { when @$! =~ Foo { ... } } I do have a few questions. 1. Does this example: { my $p = P.new; LAST { $p and $p.Done; } foo(); my $q = Q.new; LAST { $q and $q.Done; } ... } effectively get compiled into something like: { my $p; my $q; $p = P.new; LAST { $p and $p.Done; } foo(); $q = Q.new; LAST { $q and $q.Done; } ... } If not, how can we evaluate $q in the LAST block if foo() dies? Or are LASTs not handled by a magic BEGIN mechanism? Or are the LASTs converted into a BEGIN plus some run-time state variable that is only set when the LAST is encountered during execution? Or am I missing the point entirely ;-? 2. Consider the following example: for my $file ( @files ) { my $f is last { close } = open $file or next; foo($f); CATCH { default { print "foo($f) failed\n" } } } The last and CATCH blocks must be invoked at the end of each time around the for block, no? Or should I be writing: for my $file ( @files ) { try { my $f is last { close } = open $file or next; foo($f); CATCH { default { print "foo($f) failed\n" } } } } 3. Would the following execute the C<die>? When do I have to worry about "accidentally" catching control exceptions? sub ... { return if 1; fragile(); CATCH { default { die "Couldn't fragile." } } } 4. The test for "block exited successfully" is C< !$! || $!.clean >, for the purposes of the block-end handing code, correct? So KEEP is like LAST { if ( !$! || $!.clean ) { ... } } and UNDO is like LAST { unless ( !$! || $!.clean ) { ... } } in which case CATCH is actually like UNDO with an implied given, die, and $!.markclean, except it's handled in a different end- block order, yes? 5. What is the order of processing all these special blocks at the end of their containing block? Is it: 1. CONTINUE 2. CATCH 3. KEEP 4. UNDO 5. LAST 6. POST or some other fixed order, or is there some sort of order-of- encounter interleaving of some of the kinds of blocks? 6. What is the value of my $x = try { "1" CATCH { default { "2" } } LAST { "3" } }; What happens for each permutation of replacing "n" by die "n"? 7. Is there any particular reason why multiple CATCH blocks can't simply be queued in some fashion like multiple LAST blocks? Yours, &c, Tony Olekshy