Hi all,
Patrick R. Michaud wrote:
On Fri, Jul 11, 2008 at 04:49:55PM -0400, Bob Rogers wrote:
Content-Description: message body text
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).
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.
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.
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). How does it contrast with how
you would have expected to compile this, and how does this approach fit
with your HLL? Note that the code that I have attached runs and both
with and without my previous patch that you started this thread about.
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.
Hope this helps the discussion,
Jonathan
[1]
http://dev.perl.org/perl6/doc/design/syn/S04.html#When_is_a_closure_not_a_closure
.sub 'main' :main
$P0 = new 'ResizablePMCArray'
push $P0, 'a'
$P1 = new 'ResizablePMCArray'
$P2 = new 'ResizablePMCArray'
push $P2, 'simple'
push $P1, $P2
push $P1, 'test'
$P3 = new 'ResizablePMCArray'
push $P3, 'for'
push $P3, 'a'
push $P3, 'simple'
push $P1, $P3
push $P0, $P1
push $P0, 'script'
'dump_thing'($P0, '#')
.end
.sub 'dump_thing'
.param pmc thing
.param pmc prefix
.lex '$thing', thing
.lex '$prefix', prefix
$P0 = find_global 'anon_1'
$P1 = newclosure $P0
.lex '$recur', $P1
$P2 = find_lex '$thing'
$I0 = isa $P2, 'ResizablePMCArray'
unless $I0 goto not_ResizablePMCArray
$P3 = find_lex '$prefix'
print $P3
print "[\n"
$P4 = find_global 'anon_2'
$P5 = newclosure $P4
$P6 = find_lex '$thing'
'map'($P5, $P6)
$P7 = find_lex '$prefix'
print $P7
print "]\n"
goto end_if
not_ResizablePMCArray:
$P8 = find_lex '$prefix'
print $P8
$P9 = find_lex '$thing'
print $P9
print "\n"
end_if:
.end
.sub 'anon_1' :outer('dump_thing')
.param pmc subthing
.lex '$subthing', subthing
$P0 = find_lex '$subthing'
$P1 = find_lex '$prefix'
$P2 = new 'String'
$P2 = concat $P1, ' '
'dump_thing'($P0, $P2)
.end
.sub 'anon_2' :outer('dump_thing')
.param pmc topic
.lex "$_", topic
$P0 = find_lex '$recur'
$P1 = find_lex '$_'
$P0($P1)
.end
.sub 'map'
.param pmc block
.param pmc array
.local pmc result, it
result = new 'ResizablePMCArray'
it = iter array
loop:
unless it goto loop_end
$P0 = shift it
$P0 = block($P0)
push result, $P0
goto loop
loop_end:
.return (result)
.end