Keep in mind that I am only an egg, and I am putting my intuition and experience with similar languages to mind. Perl6 might be doing things differently than I expect.
On 11/22/06, Anatoly Vorobey <[EMAIL PROTECTED]> wrote:
To add some more confusion to what Yuval wrote: In general, it doesn't seem to be very clear how inner (lexically) subs see their enclosing lexical environments. Possibly I'm simply very confused, in which case un-confusing me would be much appreciated. Here're some code snippets. { my $x = something(); if $x==1 { ...code... } } In this case, when the inner block is entered from the outer as part of execution, it's pretty clear how the inner block can see $x. There can be many separate pads (local lexical environments) for the outer block (for instance, if the outer block's a sub that recursed on itself) with different values of $x, but only one of them was current just now before we entered the inner block, and we can definitely arrange that it be found as the inner block's ::OUTER.
My experience with other statically typed by extremely flexable languages is that the pads tend to be arranged in (possibly interconnected) linked lists. In this example, I see potentially three pads linked by the time ...code... is called: One containing the local variables defined in ...code..., one containing the visibly defined $x, and one visible outside that scope. A reference to $x in ...code... will traverse the linked list until it finds an $x, presumably finding the one defined in the sample code.
With a closure, that works fine, too: { my $x = something(); return { $x++; } } As the inner block is cloned, the right pad for the outer block is at hand, and $x can be copied from it (as a reference) to the snapshot being built for the inner block.
Er, that's not how I see it. When {$x++;} is evaluated as a closure, it is for all intents and purposes a function, with its own linked-list of pads. The head pad in the list contains nothing, and the next pad (the outer pad belonging to the function) contains $x. Since the head pad survives the call, and it has a reference on the outer pad containing $x, that outer pad survives as well. However, since nothing else points to it, the value of that particular $x is only visible to invokers of the closure returned.
But what about inner named subs? { my $x = something(); sub foo { $x; } } &foo is visible outside the outer block because it's a package variable. Presumably at compile time the block of foo was compiled to a Code object, and &foo in the current package now points to that Code object (or actually the Routine object storing the Code object inside it, but the difference doesn't seem to be relevant here?). All this happens before runtime starts. Now we call foo() from somewhere far away. What's it going to see as $x? Certainly not anything meaningful, because the assignment to $x might not even have run by now (if it's a part of a sub that was never called), or it may have been run in many versions.
If I understand things, the sub foo {$x;} is not actually compiled into a callable function until run time. At which time, a pad containing $x exists, which can be referenced by sub when converting {$x;} into a Code object bound to the package variable foo. My suspicion, without testing, is either (a) sub foo {$x;} in that context doesn't actually define a package variable; (b) running that block twice will redefine the package variable foo to the new defintion (i.e., the new pad list); or (c) the redefinition will give an error for redefining a function.
So it seems safe to say that foo() should see undef here as the value of $x (perhaps because its idea of ::OUTER is really the compile-time version of the outer pad). Now how about this:
I disagree. foo should either be undefined (because the sub foo {$x;} hasn't been run yet) or foo() should have the value of something(), since it is pointing to an $x that was given that value.
{ my $x = something(); sub foo { $x; } foo(); } Presumably in this case we do want foo() to see the right $x, current at the time. How can it find that pad as its ::OUTER? More importantly, if in the example when foo() is called from far away it sees $x as undef, how can the following work at all (I'm assuming it should work)? At package-level: my $x = 1; sub foo { $x; } Now we call foo() from a different package and we expect it to see $x==1. But how is the situation different?
It's the scope. The basic scoping rule I expect, regardless of actual implementation, for a lexically scoped language, is that a variable should refer to the closest lexically scoped variable declaration of that name. In all these cases so far, that would be to the $x declared in the lexical scope the definition of foo is in. I would expect that this would work: my $x = 0; sub foo { ++$x; } sub bar { my $x = 0; return {++$x;} } sub baz { my $x = 0; sub quux { ++$x; } } print foo(); # 1 print foo(); # 2 print bar()(); # 1 print bar()(); # 1 quuux = bar(); print quuux(); # 1 print quuux(); # 2 baz(); print quux(); # 1 print quux(); # 2 I figure one of us is wrong, and we've stated exactly what we think should happen, so either way, someone will correct one of us.