From: "Patrick R. Michaud" <[EMAIL PROTECTED]> Date: Fri, 11 Jul 2008 16:23:28 -0500
On Fri, Jul 11, 2008 at 04:49:55PM -0400, Bob Rogers wrote: > Based on what Bob has been saying, I can't now think of a case where > an inner closure _shouldn't_ go ahead and have its outer_ctx set > whenever an outer sub is invoked . . . > > <honk>Foul!</honk> That's exactly what r28763 does to break my code. Okay, I give up. I don't see exactly how .sub 'outer' .const 'Sub' inner_sub = 'inner' inner_sub.'capture'() ... is substantially different from .sub 'outer' .const 'Sub' inner_sub = 'inner_sub' $P0 = newclosure inner_sub set_global 'inner', $P0 ... In each case, the capture is occurring at the beginning of the outer sub invocation, and the 'inner' symbol table entry points to a Closure that has 'outer' as its outer context. Absolutely, but that's not where the problem lies. The problem is that r28763 did so implicitly and unconditionally, overwriting anything newclosure might have done. If you meant "have its outer_ctx set I<explicitly> whenever an outer sub is invoked", then I apologize; I misunderstood you. (And in any case, it was rude to have honked.) Also, the Perl 5 examples you're providing don't really help me understand the issues much, as I've never done a lot of work with closures in Perl 5, they don't map exactly to Perl 6, and Perl 5's notion of symbol table handling is a fair bit different from Parrot and Perl 6. I really need to see examples of PIR that don't work, or to have the Perl 5 translated into what you think the equivalent PIR should be. Pm Sorry; I was trying to get the reply off without spending too much time on it. ================ From: "Patrick R. Michaud" <[EMAIL PROTECTED]> Date: Fri, 11 Jul 2008 17:19:20 -0500 On Fri, Jul 11, 2008 at 04:49:55PM -0400, Bob Rogers wrote: > This is certainly not the case for recursive subs. Consider the > attached example, a lightweight Perl 5 dumper. (It is slightly > artificial to break the recursive step out into a sub, but that might > make sense after adding hash support.) We need a distinct closure for > each level of dump_thing call, lest $prefix refer to the wrong thing. > And we need to call those closures from registers, to be sure that we > are calling the right one. In the attached example, I think the lines > my $recur = sub { > my $subthing = shift; > > dump_thing($subthing, $prefix.' '); > }; guarantee that $recur gets a distinct closure for each level of call, since a closure is cloned upon assignment. In other words, the assignment is where a separate newclosure would be performed (or, in my example, simply cloning the closure directly). Pm Yes; this was intended as an example where "autoclose" clearly does not work. In this case it's easy for a compiler to prove that, but I wanted to produce an example here in order to make it easier to visualize cases where proving downwardness is hard: Does it call something else that calls dump_thing? Might it be running simultaneously in two threads? But I must confess that I still don't understand what it means not to "clone" a closure. What is an "uncloned closure" anyway? Will @Larry deign to speak? ================ From: Jonathan Worthington <[EMAIL PROTECTED]> Date: Sat, 12 Jul 2008 01:11:12 +0200 Hi all, Patrick R. Michaud wrote: > . . . > guarantee that $recur gets a distinct closure for each level > of call, since a closure is cloned upon assignment. In other > words, the assignment is where a separate newclosure would be > performed (or, in my example, simply cloning the closure directly). This is consistent with my view of the specified Perl 6 semantics[1] for closure handling. I translated Bob's Perl 5 example into PIR and put newclosure where the Perl 6 specification would suggest to put it and it produces the same output as the Perl 5 script. This is without doing *any* newclosure calls prior to a call, just some when we are taking a reference. Thank you very much for doing the translation; it is more or less the way I would have done it. (If I had known you were going to do this, I could have given you my Perl script for generating PIR that builds these stupid sample data structures; I had a bunch to do for my YAPC talk!) Pm: I hope the PIR helps you understand Bob's example better. Notice that we do newclosure whenever a block is referenced (where by block I mean anything compiled down to a Parrot .sub) and the reference contains the new closure, which makes sense because we're taking a closure (recording a snapshot of it at that point) to invoke later. Yes, but I instead of "recording a snapshot" (there's that word again), I would say "capturing the current :outer context", or some such. And, I would also point out that declaring the $prefix closure as a lexical enables other lexical subs in the same scope to call the correct sibling closure. Bob: I hope this hand-compilation of your example with the Perl 6 semantics applied helps you see where Pm is coming from; happily, it also matches it's semantics when run in Perl 5. So I think we can make this example work just fine, without having to replace stuff in the symbol table (which is, well, awkward). I'm afraid you misunderstood; as I said above, I just wanted to give an example where "autoclose" could not work. Let me try to clarify: The only reason I mentioned stuffing closures back into the symbol table was to show that "autoclose" is not necessary. (I failed, though, because Patrick correctly pointed out that "autoclose" is currently required for methods, with or without the "multi-", though I consider it a bug that you *have* to autoclose in these cases; I'm not convinced that can always be made to work.) Then there was some back and forth about interpreting S04 on when and how closures are "cloned" in cases where a lexical sub is defined globally; that is still murky, but somewhat tangential. In all cases, I was trying to solve (or at least understand) Patrick's problems; none of the closure-in-the-namespace stuff is relevant to Kea Common Lisp. (Or any other Parrot language that doesn't rely on autoclose.) How does it contrast with how you would have expected to compile this, and how does this approach fit with your HLL? The only thing I think I would have done differently would be to inline the map, but that's just fussiness on my part. (In Lisp, the natural idiom would be "(mapc recur thing)"; Perl 5 requires an indirection because of the $_ vs $@ distinction.) But the strategy is identical to what Kea-CL does. I won't show that code, though; it's much harder to read. Note that the code that I have attached runs and both with and without my previous patch that you started this thread about. That puzzles me. I thought your translation might fail if you inlined map, but that is not the case. I have no theory. Note that this doesn't give any great answers yet about what happens when we do newclosure on a multi, but I think we can make that do something sane (snapshot the lot, perhaps? Need to think about what falls out of that...) Also it's interesting to note in this example that replacing "newclosure" with "clone" gives the same semantics. IMHO, it does not make any sense to do newclosure on a MultiSub; I cannot imagine what that might mean. On a multimethod, certainly, but individual methods are likely to have distinct lexical environments, -- and newclosure is all about establishing the right lexical environment. So that will require separate newclosure operations for each method (that needs it, of course). Hope this helps the discussion, Jonathan It does; thanks for speaking up. -- Bob [1] http://dev.perl.org/perl6/doc/design/syn/S04.html#When_is_a_closure_not_a_closure