End-of-scope actions: POST blocks.
Nicholas Clark wrote: It makes them far more useful as tidy up things if they are tacked on at runtime, not compile time. If I understand, it is proposed that code like this: { Alpha; POST { Beta }; Gamma; POST { Delta }; Epsilon; } will behave something like this: { my @onEnd = (); try { Alpha; unshift @onEnd, sub { Beta }; Gamma; unshift @onEnd, sub { Delta }; Epsilon; } finally { foreach (@onEnd) { {$_}(); } } } If so, then I have some observations. - It does have in-flow presence, so it doesn't suffer from the problem that "always" has; POST is a statement, not a dangling clause. That fixes my main complaint with RFC 119. On the other hand, now there's nothing at the end of the scope to tell you whether or not have to revisit the whole scope to check if there are any POST clauses before you advance your mind to the next statement. Hmm. - It does solve the dual free problem. Where RFC 88 says: try { my $p = P-new; my $q = Q-new; ... } finally { $p and $p-Done; } finally { $q and $q-Done; } the proposed method could say: my $p = P-new; POST { $p-Done; }; my $q = Q-new; POST { $q-Done; }; ... On the other hand, there is no easy way to specify the order in which $p-Done and $q-Done are invoked, independent of the order in which $p and $q are constructed. Hmm. - The semantics aren't quite the same as "try", which declares exception handling blocks that are active once try's block is entered, not when the declarations are passed in the block. Perhaps both mechanisms should be supported? RFC 88 does suggest that RFC 119 should be considered independent of RFC 88, not as an alternative thereto. - If the contents of a POST block raises an exception, how does it affect other POST blocks? In the example above, if $p-Done throws, then $q-Done should be called, *and* vice versa, *and* unwinding should propagate if either one threw (or anything else did for that matter, unless it was caught without the catching throwing). - What about CATCH blocks (what RFC 88 calls "catch" and RFC 119 calls "except")? What exactly is the interaction between these dynamic POST and CATCH blocks? We will need a detailed semantics, along the lines of http://www.avrasoft.com/perl6/rfc88.htm#Unwinding_Semantics - What about conditional CATCH blocks? What syntax can we use that interacts reasonably well with the rest of Perl? - What's the return value? With RFC 88 you can say: my $r = try { f() } catch { 0 }; What are the syntax and semantics in the CATCH/POST case? Perhaps something like: my $r = do { CATCH { 0 } f() }; Hmm. Yours, c, Tony Olekshy
Re: End-of-scope actions: POST blocks.
On Mon, Feb 12, 2001 at 01:58:57PM -0700, Tony Olekshy wrote: - It does have in-flow presence, so it doesn't suffer from the problem that "always" has; POST is a statement, not a dangling clause. That fixes my main complaint with RFC 119. On the other hand, now there's nothing at the end of the scope to tell you whether or not have to revisit the whole scope to check if there are any POST clauses before you advance your mind to the next statement. Hmm. You seem to like a /lot/ of context markers for line-of-flow-control. I think that's somwhat misguided. Perl already has a fair number of out-of-line control semantics. DESTROY, overloaded operators, and other CAPITAL subs come to mind. Moreover, I see no need to declare at the beginning, middle, and end of a block that there's some exception handling/out of order code going on. If you want to understand a block of code, reading everything textualy within the curley braces should be a _minimum_, in which case, the word POST is plenty. Also, I think that the syntax of exception processing should be minimial, not only because I'll use it more if it's an extra 5 keystrokes instead of an extra 15, but because the important things should stand out more -- that's why we support both leading and trailing ifs. - It does solve the dual free problem. Where RFC 88 says: try { my $p = P-new; my $q = Q-new; ... } finally { $p and $p-Done; } finally { $q and $q-Done; } the proposed method could say: my $p = P-new; POST { $p-Done; }; my $q = Q-new; POST { $q-Done; }; I like this MUCH better. Even better (IMHO) would be: my $p = P-new; POST: $p-Done; my $q = Q-new; POST: $q-Done; That only gets rid of one char (the ; after the -Done;s is already optional as the last statement in a block), but it looks much less crowded to me. I'd get rid of the caps on POST if I could, but that would be breaking tradition for naming magical things. On the other hand, there is no easy way to specify the order in which $p-Done and $q-Done are invoked, independent of the order in which $p and $q are constructed. Hmm. You could always say: my $p = new P; my $q; try {$q = new Q}; POST: $q-Done; POST: $p-Done; A little on the messy side, but workable. - The semantics aren't quite the same as "try" TMTWTDI. You could even have { my $a = new A; POST: $a-Done; ...; } catch { f() } finaly { g() }; (In which case, we go with order-encountered: first the POST, then the catch (if approps), then the finaly. (I don't think there should be a try keyword neccessary at the top. (Though if present, it should be a warning not to have any danglers on the end of the block.)) - If the contents of a POST block raises an exception, how does it affect other POST blocks? I'd like to poist this rule: Each POST block is independent. If one raises an exception, it has no effect on the others (including the value of @@). This is equivlent to saying that each POST block has an implicit eval {}. If you want to do somthing if a POST {} block throws, use POSTs inside the POST, or any of the other try {} catch {} finaly {} things. In the example above, if $p-Done throws, then $q-Done should be called, *and* vice versa, *and* unwinding should propagate if either one threw (or anything else did for that matter, unless it was caught without the catching throwing). I don't follow you, neccessarly. Is that what I just said? I'm a little bit behind in the jargon. - What about CATCH blocks (what RFC 88 calls "catch" and RFC 119 calls "except")? What exactly is the interaction between these dynamic POST and CATCH blocks? We will need a detailed semantics, along the lines of http://www.avrasoft.com/perl6/rfc88.htm#Unwinding_Semantics I'd go with a textual FIFO for danglers (what you have proposed, if I read it correctly, then a LIFO on the in-body ones in order encountered. BTW, when I say order encountered what I mean is that for this: for (1, 2) { POST {print "2\n"} if ($_ == 2); POST {print "1\n"} if ($_ == 1); print "$_\n"; } Will output: 1 2 2 1 because we hit the 1 POST block in order-of-execution first, even though it was second in textual order. - What about conditional CATCH blocks? What syntax can we use that interacts reasonably well with the rest of Perl? Hm? POST { if (cond) { somthing } }. CATCH is just shorthand. - What's the return value? With RFC 88 you can say: The return value is undef (or empty-list) until you hit a return statement. If the code dies before returning, then it stays undef/() unless somthing run after that (IE a CATCH/POST/catch/finaly/whathaveyou) returns. (And if it does, that blocks all execption processing.) You can mess with the return arbitrarly if we support return-stack visiblity. -=- James Mastros -- "All I really want is somebody to curl up with and pretend
Re: End-of-scope actions: POST blocks.
James Mastros wrote: You seem to like a /lot/ of context markers for line-of-flow-control. I think that's somwhat misguided. I have not anywhere suggested that I'm against POST blocks; in fact RFC 88 supports the similar "always" concept mentioned by RFC 119. I'm just trying to figure out exactly how they should work. Moreover, I see no need to declare at the beginning, middle, and end of a block that there's some exception handling/out of order code going on. Agreed. I don't want to go overboard either. I just want to extend "eval" to become "try" (hey, saves you a character James), so it can support the semantics of auxiliary catch and finally clauses, while still leaving good-old eval alone. Also, I think that the syntax of exception processing should be minimial [...] because the important things should stand out more -- that's why we support both leading and trailing ifs. Here I disagree. I think indicating the presence of an exception handling scope should be made to stand out quite a lot, because I do think it's quite important. It's, well, exceptional. - It does solve the dual free problem. Where RFC 88 says: try { my $p = P-new; my $q = Q-new; ... } finally { $p and $p-Done; } finally { $q and $q-Done; } the proposed method could say: my $p = P-new; POST { $p-Done; }; my $q = Q-new; POST { $q-Done; }; I like this MUCH better. So do I, that's why I mentioned it as an advantage of the technique. As long as the order in which you want the Done()s to be called matches the order defined by the semantics of multiple POST blocks. I'd get rid of the caps on POST if I could, but that would be breaking tradition for naming magical things. Well, we do use lower case "continue". Perhaps lower-case is just fine for block-level things. I did use "always" in the updated reference implementation. - The semantics aren't quite the same as "try" [...] TMTWTDI. You could even have { my $a = new A; POST: $a-Done; ...; } catch { f() } finaly { g() }; (In which case, we go with order-encountered: first the POST, then the catch (if approps), then the finaly. Yes, that's how I did it in the updated reference implementation. - If the contents of a POST block raises an exception, how does it affect other POST blocks? I'd like to poist this rule: Each POST block is independent. If one raises an exception, it has no effect on the others (including the value of @@). This is equivlent to saying that each POST block has an implicit eval {}. If you want to do somthing if a POST {} block throws, use POSTs inside the POST, or any of the other try {} catch {} finaly {} things. Even easier, in the updated reference implementation I just dyamically convert a POST into a finally and now all of finally's already well- defined semantics answer my own question. In the example above, if $p-Done throws, then $q-Done should be called, *and* vice versa, *and* unwinding should propagate if either one threw (or anything else did for that matter, unless it was caught without the catching throwing). I don't follow you, neccessarly. Is that what I just said? I'm a little bit behind in the jargon. It's close to what you just said, with the proviso that you must keep track of $@ after each of the implied evals and then do the equivalent of die @$ if any of them were true. Things are more complicated with catches, because a catch that doesn't die means the implied trailing die *doesn't* get invoked after the post blocks are finished. I'd go with a textual FIFO for danglers (what you have proposed, if I read it correctly, then a LIFO on the in-body ones in order encountered. That's what I did in the updated reference implementation, except that I do the in-body ones before the statement-level clauses. - What about conditional CATCH blocks? What syntax can we use that interacts reasonably well with the rest of Perl? Hm? POST { if (cond) { somthing } }. CATCH is just shorthand. Say the code before the post throws. Now, if cond is true (and somthing doesn't throw) you do not want to unwind after the post, because the exception has been cleanly caught. But if cond is false (or somthing does throw) you do, because it hasn't been. How do you indicate this using the above notation? During the RFC process, there was a great discussion about this, with one camp suggesting that post/finally shouldn't propagate trapped exceptions, because you can always do it explicitly in every post/finally you write. Others considered that to be a dangerous proposal, because of how easy it would be to forget the re-throw in the common case. The approach taken by RFC 88 was to work out a syntax and semantics for multiple conditional catch clauses that still makes the easy easy the helps make the hard possible. In the updated reference implementation, I dynamically convert
Re: End-of-scope actions: POST blocks.
"David L. Nicol" wrote: POST{stuff} is a macro for push (my) @Deferred_stuff, sub {stuff}; # my on first use in a space Since the reference implementation requires try, @Deferred_stuff is actually try's argument list (a bunch of tagged catch and finally blocks). The "my" is provided by try. But in general, yes, if post/always/except is part of Perl 6 then every block will have to keep track of whether or not any such action has been seen during the excecution of the block. Fortunately, it need do nothing special if no such actions have been seen (such as, oh, GC for example). and return(arg) becomes, including implied EndOfBlock return: {shift @Deferred_stuff} while @Deferred_stuff; return(arg) Yes, but you have to keep track of exceptions raised while executing {shift @Deferred_stuff}, so you can propagate them if they occur, and you have to keep track of catch clauses that don't raise exceptions, because they can terminate propagation. For example: my $r = do { except { h() }; f() }; g(); or my $r = try { f() } catch { h() }; g(); should set $r to f() unless f() raises an exception, in which case it should use h() for $r, and whether or not f() raises an exception it should *not* propagate such an exception, it should execute g(). Unless, of course, h() throws, in which case it should propagate that exception, not set $r, and not call g(). On the other hand: my $r = do { always { h() }; f() }; g(); or my $r = try { f() } finally { h() }; g(); should call h() whether or not f() throws; and if f() or h() throw the exception should be propagated, otherwise $r should be set, and g() should be called. Yours, c, Tony Olekshy