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. 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. I'll try to phrase it in the terminology you've been using (and apologies if I slip up). 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. 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. 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. Thus "newclosure" doesn't really do what I want, because it always creates a new Closure PMC, and then I have to somehow get that new PMC into its correct sub/method/multisub location. What I really want is a way to give an existing Closure PMC an outer_ctx (but not automatically as part of invocation). So, I propose that we add a "capture" method to Closure PMCs to set the outer_ctx for that PMC. This is roughly equivalent to what 'newclosure' does now, but doesn't clone the Closure PMC prior to capturing the variable bindings. This capture method would be called from the outer sub at whatever point we would otherwise be doing a "newclosure+store" operation. But now since we're not creating a new PMC, we don't have to worry about trying to fix up the symbol table, method table, or MultiSub entry that references the current Closure. (Side note: we _could_ make this a 'capture' opcode instead of a method, but I'm not a big fan of opcodes that only work on one PMC type. In fact, I often wonder if the 'newclosure' opcode should really be a method on Capture, since (at present) it only works on Capture PMCs. OTOH, perhaps an opcode is much faster than a method call, and perhaps capture/newclosure warrant speed here.) In fact, with a 'capture' method or opcode, we might not need 'newclosure' at all. A newclosure operation could potentially be done by: $P0 = clone $P0 # clone the Capture PMC $P0.capture() # capture outer context into the clone But this is a feature, and we can certainly keep 'newclosure' around if we want to do a clone+capture sequence. Just having something like the capture method described above seems like it would solve my problems without resorting to autoclosures. The PIR for my Perl 6 example could then look something like: .sub 'foo' .param pmc x .lex '$x', x .const 'Closure' inner = 'inner' ## capture variable bindings for inner inner.'capture'() print "outer foo " say x 'inner'() .end 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. If you and others think this is workable, then this is good enough for me. But there's more. :-) 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. 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. There might be a small optimization to be had if we can determine that some inner closure can't be invoked (and thus we don't need the capture operation), but I'm thinking that the cost of immediately assigning outer_ctx to all inner subs is far less than doing separate newclosure+store operations for each. (Perhaps there's another reason this approach can't work.) Anyway, that's my proposal for now -- feel free to shoot holes in it or tell me where I've overlooked something or misunderstood closures once again. :-) Pm