From: "Patrick R. Michaud" <[EMAIL PROTECTED]> Date: Fri, 11 Jul 2008 01:55:11 -0500
On Thu, Jul 10, 2008 at 11:06:55PM -0500, Patrick R. Michaud wrote: > > I _think_ [methods and subs] are the only two cases where we > have to do something like this, and I guess they aren't too onerous. I was wrong, the third case is MultiSubs, and at the moment it's _very_ onerous, because we can't just replace things in the symbol table. Yes, I think we are stuck with that for now. But having given myself permission to take a break, I think I may have come up with a workable answer that does almost everything we (both) want . . . If I understand your messages correctly, the main problem with "autoclosures" is the "auto" portion -- i.e., trying to capture variable bindings automatically at the time a closure is invoked, and doing this only once on the closure's first invocation. Let's agree that this behavior is wrong. No dissent here. Let's go even further and explicitly make this an error, such that invoking a Closure PMC where its outer_ctx is null throws an exception. You are preaching to the choir. ;-} However, I'd still like the capability to give an existing Closure an outer_ctx without having to replace it in the symbol table, method table, or MultiSub PMC . . . To repeat, in this scenario, invocation never does a capture on its own -- that's always up to the outer sub to handle by doing either a newclosure or capture operation prior to invocation. I think this sounds like a good thing. However, there is a further problem with "autoclose" that had completely slipped my mind [1]: The "autoclose" feature assumes that there will only ever be one active closure at a time for a given sub. This is a pretty serious limitation, and I apologize for not mentioning it sooner. 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. So that takes us back to what I said about considering "autoclose" as an optimization, to be used only if the compiler can prove that it is safe. And since it can be difficult to prove that a sub is not recursive (since it may recur mutually with a sub in another compilation unit), and impossible to prove that it won't be invoked re-entrantly from another thread, I have to conclude that "autoclose" is a solution in search of a problem. (So Leo's and chromatic's instincts prove to be correct.) Unfortunately, this hits squarely at the heart of your desire not to re-install a new closure in the namespace (when the sub has to be invokable that way). It may still make sense to make incremental improvements to autoclose, but if I were in your shoes, I would remove all use of autoclose from PCT and Rakudo, replacing it with a general solution, and reintroduce it carefully only when both were closer to production and speed is important. . . . Instead of having Parrot do autoclosures -- i.e., automatically capture outer_ctx when an inner Closure is invoked -- perhaps we could have a way for an outer sub to automatically perform the captures for all of its inner subs. In other words, every sub would maintain a list of its inner subs (automatically created from the inner subs' :outer flags), and then the outer sub can automatically set the outer_ctx for all of its inner closures. This could happen either as part of the outer sub's invocation, or it could be an explicit instruction or method call generated by the HLL compiler. How about putting an :auto pragma on the inner subs? That would allow it to be done transparently in C as part of sub calling, and would give a fairly fine degree of control to the compiler. (Assuming we want to keep drinking this Kool-Aid, that is. ;-) 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. (Not to mention the attached example. ;-) But I won't make you sit in the penalty box; it's just too damn hard to keep all this in one's head at the same time. -- Bob [1] The fact that my mind has been foggy with headaches on and off for the past three days probably has something to do with it.
#! /usr/bin/perl -w use strict; use warnings; sub dump_thing { my ($thing, $prefix) = @_; my $recur = sub { my $subthing = shift; dump_thing($subthing, $prefix.' '); }; if (ref($thing) eq 'ARRAY') { print($prefix, "[\n"); map { $recur->($_); } @$thing; print($prefix, "]\n"); } else { print "$prefix$thing\n"; } } dump_thing([qw(a), [['simple'], 'test', [qw(for a simple)]], 'script'], '# ');