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