Be Careful With state Variables
I use state
variables a lot. They're great for creating a private cache in a method. However, I was recently asked about using them with Roles. What happens when the role methods are flattened into separate namespaces? I suspected that they would share their state, but I wasn't sure, so I wrote a quick check.
This code is not brilliant, but it does show the danger:
use 5.10.0;
{
package My::Role;
use Moose::Role;
sub has_state {
my ( $self, $value ) = @_;
state $some_val = $value;
return $some_val;
}
}
{
package Object1;
use Moose;
with 'My::Role';
}
{
package Object2;
use Moose;
with 'My::Role';
}
my $first = Object1->new;
my $second = Object2->new;
$first->has_state(7);
$second->has_state(2);
say $first->has_state;
say $second->has_state;
That will print 7
twice, even though you may have been hoping that it didn't. So state
variables can be thought of as lexically scoped globals. Fortunately there was a quick fix for this person's issue, but be careful when using state
variables.
It's not all that dis-similar to
my
orour
really. See this gist.The more I think about this the more I think that those kind of state variables should really be attributes, no?
As Toby so nicely demonstrated, they aren't so different from file-scope lexicals and we all know how those work.
Yes, state variables are not as magical as one might hope:
Yes, these two variables are essentially identical:
You get one instance of the state variable for each instance of the sub which the variable is inside of.
For named subs, the sub is instantiated once, at parse time, and bound to a glob in the package. No matter how many times you take a reference to that sub, it's still the same sub, so there's only one copy of the state variable.
With anonymous functions, though, you can not only take a reference to existing instance, but you can create new instances too. Each you do so, the new instance will also have a distinct copy of the state variable.
It works exactly the same way as a closure.
The difference is that with a closure you would have to declare the closed-over variable just outside the closing-over scope, whereas calling it a state variable allows you to keep the declaration within a much narrower scope.
The reason that doesn’t work is because of the correspondence of state variables to closed-over variables. Their persistence semantics are tied to function scopes specifically, not to generic scopes. Both of the state variables in your example are in the file scope, so both of them persist the same way, even though one is in a narrower scope than the other.
The fact that
state
is tied to function scopes is why it provides any value as syntactic sugar at all. If it were tied to regular scopes it would be no different frommy
.So if you need generic scoping, as in your example, why aren’t you just using plain lexicals?