From: "Patrick R. Michaud" <[EMAIL PROTECTED]> Date: Fri, 3 Aug 2007 21:51:53 -0500
Perhaps ignore my earlier message -- this one is more coherent. I haven't gotten that one yet. (Must have been via RT. Now if I could only remember to send all my incoherent posts via RT. ;-) On Fri, Aug 03, 2007 at 03:37:39PM -0700, Bob Rogers wrote: > sub make_closures_loop { > # Return $n closures, each with lexical references to $i and $n. > my $n = shift; > > my @result; > for (1..$n) { > my $i = $_; > push(@result, sub { print "Called sub $i out of $n.\n"; }); > } > return @result; > } > > Currently, the only way to get a distinct binding for each "$i" in > PIR is to factor the loop body out into a separate lexical sub. This is > far from ideal, not least because it is not transparent to the HLL user. Factoring the loop body out into a separate lexical sub is _exactly_ how Chip described to me that the above needed to be done. Or, phrased differently, every lexical scope ends up requiring its own parrot sub . . . Bummer. I'm not sure why this separate lexical sub has to be visible to the HLL user -- the compiler ought to be able to make it appear as though the sub isn't present. (But I also bet that we can come up with an example that makes it really hard to do that.) It will show up in backtraces, if nothing else. This may complicate the user's debugging session slightly, because what the user assumed would be one call frame is actually two, and inner functions may not have the expected compiler-assigned names, but that's hardly a show-stopper. > Parrot should do better, IMHO. The easiest way, it seems, would be > to resurrect the push_pad and pop_pad instructions in some form. [...] I'd like for Parrot to be able to do better, yes, but I'm not yet enough of an expert on closure handling to be able to refute Chip's reasons for designing things this way. The current model has the advantage of being more declarative, which is good; things that can be nailed down at compile time should be. But the same thing could be done for multiple loop scopes as well. Consider the following alternative: ## Return n closures, each with lexical references to "I" and "N'. .sub make_closures_loop .param pmc n .lex "$n", n .local pmc result result = new .FixedPMCArray $I1 = n result = $I1 .const .Sub $P53 = 'internal_make_closures_loop_0' $I0 = 0 next: if $I0 >= $I1 goto done .push_scope .lex "$i", $P42 $P42 = new .Integer $P42 = $I0 inc $P42 newclosure $P52, $P53 result[$I0] = $P52 inc $I0 .pop_scope goto next done: .return (result) .end Every .lex belongs to the immediately enclosing .push_scope/.pop_scope group (or the sub top-level scope). .push_scope needs to expand into push_pad, and .pop_scope needs to do a pop_pad, but the pseudo-ops serve to delimit scopes so that the PIR compiler can build a LexInfo object for each scope. (But the inner ".lex" ops may require an explicit store_lex, since they are not being stored in the Parrot_Context. (Which is a good thing.)) I also found the push_pad and pop_pad model somewhat easier to grasp conceptually, but I recall Chip was fairly certain that they wouldn't handle things in the generic case. They would certainly permit more to be handled than is handled now. And re-adding them should be monotonic in terms of Parrot functionality. Which means that Chip's statement (IYRC) seems to make no sense. ??? FWIW, the perl6 compiler currently treats _every_ block as a new lexical scope, and generates a separate Parrot sub for each . . . Hmm. The "lexical scope == Parrot sub" worldview seems backwards to me. But probably I'm just grumbling because I've realized I'm going to have to rewrite a fair-sized chunk of my compiler to cope with this. Oh, well; so be it. ================ From: "Patrick R. Michaud" <[EMAIL PROTECTED]> Date: Fri, 3 Aug 2007 22:13:25 -0500 Just for completeness, attached is a PIR version that I think is slightly more faithful to the original test-closures.pl program. It appears to work: $ ./parrot test-closures-3.pir Called sub 1 out of 3. Called sub 2 out of 3. Called sub 3 out of 3. $ Pm Makes sense; looks like what "map" would compile into, if the block were not inlined. Thanks for explaining the situation. -- Bob