From: chromatic <[EMAIL PROTECTED]>
Date: Wed, 9 Jul 2008 13:59:16 -0700
I read that in the lexicals PDD, and I think the current behavior is
bizarre *without* the call to newclosure. How is it even possible to
close over a lexical environment in an outer when that lexical
environment was never even created?
It's difficult to describe that as anything other than ridiculous.
That was my initial reaction, too . . .
Consider Bob's example Perl 5 code:
sub outer {
my $x = shift;
print "outer foo $x\n";
sub inner {
print "inner foo $x\n";
}
}
# Note that it is not illegal to call inner before outer.
inner();
There's a compile-time warning here, namely that $x will not stay
shared. Even though it looks like inner() should close over
outer()'s $x per Perl 5's scoping rules (ignoring that you can't nest
subs in Perl 5), there is no $x available at the call to inner().
Yes. I would not want Perl 5 code like that in *my* repository. ;-}
If you wrote this instead:
sub outer {
my $x = shift;
print "outer foo $x\n";
return sub {
print "inner foo $x\n";
}
}
my $inner = outer( 10 );
$inner->();
... then Perl 5 effectively performs a newclosure action, attaching
the active lexpad to the new instance of the subroutine reference.
Yes. And if you want to preserve the external API, you could even do
something like
*inner = sub {
print "inner foo $x\n";
};
which requires 'no strict', but (a) avoids the "not stay shared" warning
and (b) updates inner() every time outer() is called, as Patrick wants
in PIR. (IMHO, this is what your example *should* mean.)
I suspect the motivation for the bizarreness of the specification is the
desire to make code like this work in Parrot:
{
my $x;
sub set_x { $x = shift }
sub get_x { return $x }
}
... except that there's no real way in Parrot right now to create an
enclosing lexical block, activate it, and attach it as the outer to
one or more Closures.
-- c
FWIW, I don't consider this bizarre; I've seen production code that does
just this in order to share internal state between API functions. What
would be bizarre is expecting set_x or get_x to be able to do anything
meaningful before the outer block is executed.
And it does work now, albeit with a little indirection; see
bank-account.pir, attached. Or do you not consider this a "real way"?
At any rate, Leo said [1] that "autoclose" was something Audrey
wanted, so I assumed it was here to stay. I'd just like to keep it from
spreading. ;-}
-- Bob
[1]
http://groups.google.com/group/perl.perl6.internals/browse_thread/thread/41dbfee7b7b5bbe7
.namespace ['Foo']
.sub outer
.local pmc x
.lex '$x' ,x
.const .Sub setter = 'raw_set_x'
setter = newclosure setter
set_hll_global ['Foo'], 'set_x', setter
.const .Sub getter = 'raw_get_x'
getter = newclosure getter
set_hll_global ['Foo'], 'get_x', getter
.end
.sub raw_set_x :outer('outer')
.param pmc new_x
store_lex '$x', new_x
.return (new_x)
.end
.sub raw_get_x :outer('outer')
.local pmc new_x
new_x = find_lex '$x'
.return (new_x)
.end
.sub init :init
outer()
.end
.sub main :main
set_x(1)
$P0 = get_x()
print $P0
print "\n"
set_x(9)
$P0 = get_x()
print $P0
print "\n"
.end