on Sat, 07 Jul 2012 11:35:49 PDT:
> On Sun Jul 01 14:23:26 2012, sprout wrote:
>> ...[W]e have (at least) two
>> variants:
>>
>> • Lexical subs close over variables when the name comes into scope
>> (which could happen multiple times with loops).
>> • Lexical subs close over variables when referenced or called.
>> • Lexical subs close over variables when *first* referenced or called.
>>
>> For the most part, there is no observable difference here, until it
>> comes to referential identity and for loops.
>>
>> I don’t think we have to worry about referential identity, since
>> optimisations would make all three variants nearly identical in
>> that regard.
> Actually we do.
> State variables are not persistent across anonymous subroutines.
> push @subs, sub { state $x } for 1..10;
> will give you ten subroutines, each with a different $x.
> I find that to be completely counterintuitive. (It is also completely
> undocumented.)
Not so. Camel v4, chapter 7, page 324:
Finally, when we say that a state variable is initialized only once, we don’t mean
to imply that state variables in separate closures are the same variables. They
aren’t, so each gets its own initialization. This is how state variables differ from
static variables in other languages.
Larry was quite certain about that part, because he wanted to make sure
people understood how it worked, and that it did so by deliberate intent:
each closure is meant to get *its own* copy of a state variable.
> It means that ‘state $x’ might only create one variable, or might
> create more than one, depending on what type of sub it is defined in.
True.
> It also creates yet another set of scoping rules for people to remember.
> Flip-flops are shared between clones, so why not state variables?
Because it was designed to work this way.
> The problem it raises for lexical subs is that it is not at all clear
> when my sub foo { state $x } will create a new $x.
If we view C<my sub> as a declaration, than I cannot see why there
would ever be more than one of them, and therefore, more than one $x.
Are you worried about this situation?
sub outer {
my @subs;
for $i (1 .. 10) {
my sub inner {
state $x = rand();
return [ $i => $x ];
}
push @subs, \&inner;
}
return @subs;
}
The important question here is whether all those $x variables have
the same random number, or whether they have different ones.
And no, I don't know what the right answer is. I do agree that
is the right question, though. :)
> If state variables were intended to replace this sort of thing:
> {
> my $x;
> sub fooer {
> sub { ++$x; foo($x); }
> }
> }
> then they have failed to do so.
I don't believe they were.
> Is this something we can fix, or will it break too many things?
I'm not sure "fix" is the operative term here, considering that
it would certainly break existing code.
my @subs;
for my $decade (0 .. 10) {
push @subs, sub {
state $counter = 10 * $decade;
return $counter++ % 10;
};
}
There's also the argument that breaking this changes the
designed intent of state. And the documentation.
Can't see a good argument for all that.
Nonetheless, I can see your conundrum. I don't suppose you
have a current rakudo/perl6 and could check out how/whether
it works there? S04 has this:
http://perlcabal.org/syn/S04.html
There is a new state declarator that introduces a lexically scoped
variable like my does, but with a lifetime that persists for the life
of the closure, so that it keeps its value from the end of one call to
the beginning of the next. Separate clones of the closure get separate
state variables. However, recursive calls to the same clone use the
same state variable.
And later on:
The semantics of INIT and START are not equivalent to each other in the
case of cloned closures. An INIT only runs once for all copies of a cloned
closure. A START runs separately for each clone, so separate clones can
keep separate state variables:
our $i = 0;
...
$func = { state $x will start { $x = $i++ }; dostuff($i) };
But state automatically applies "start" semantics to any initializer, so this also works:
$func = { state $x = $i++; dostuff($i) }
Each subsequent clone gets an initial state that is one higher than the
previous, and each clone maintains its own state of $x, because that's
what state variables do.
And later on there is this, which is interesting but not completely
revealing, since it deals with a my not a state:
Lexical names do not share this problem, since the symbol goes out of
scope synchronously with its usage. Unlike global subs, they do not need a
compile-time binding, but like global subs, they perform a binding to the
lexical symbol at clone time (again, conceptually at the entry to the
outer lexical scope, but possibly deferred.)
sub foo {
# conceptual cloning happens to both blocks below
my $x = 1;
my sub bar { print $x } # already conceptually cloned, but can be lazily deferred
my &baz := { bar(); print $x }; # block is cloned immediately, forcing cloning of bar
my $code = &bar; # this would also force bar to be cloned
return &baz;
}
There may also be applicable points in S06, since you can my subs
in perl6, and in fact, the default is my sub not our sub:
http://perlcabal.org/syn/S06.html#Named_subroutines
Hm, have you thought about our sub? I think it is the same in perl6
as regular perl5 subs are — that is, ones interred in the package symbol
table — but I might be wrong.
--tom