declarator state

Documentation for declarator state assembled from the following types:

language documentation Variables

From Variables

(Variables) declarator state

state declares lexically scoped variables, just like my. However, initialization happens exactly once the first time the initialization is encountered in the normal flow of execution. Thus, state variables will retain their value across multiple executions of the enclosing block or routine.

Therefore, the subroutine

sub a {
    state @x;
    state $l = 'A';
    @x.push($l++);
};
 
say a for 1..6;

will continue to increment $l and append it to @x each time it is called. So it will output:

[A]
[A B]
[A B C]
[A B C D]
[A B C D E]
[A B C D E F]

Since they have a lexical scope, they are tied to the block in which they are declared.

sub foo () {
  for 0..1 {
    state $foo = 1;
    say $foo++;
  }
};
foo;  # OUTPUT: «1␤2␤» 
foo;  # OUTPUT: «1␤2␤» 

In this case, a new state variable is created every time the block that runs the for loop is entered, which is why the state variable is reset in every call to foo.

This works per "clone" of the containing code object, as in this example:

({ state $i = 1$i++.say} xx 3).map: {$_(), $_()}# says 1 then 2 thrice 

Note that this is not a thread-safe construct when the same clone of the same block is run by multiple threads. Also remember that methods only have one clone per class, not per object.

As with my, a declaration of multiple state variables must be placed in parentheses which can be omitted for a single variable.

Many operators come with implicit binding which can lead to actions at a distance.

Use .clone or coercion to create a new container that can be bound to.

my @a;
my @a-cloned;
sub f() {
    state $i;
    $i++;
    @a\      .push: "k$i" => $i;
    @a-cloned.push: "k$i" => $i.clone;
};
 
f for 1..3;
say @a;        # OUTPUT: «[k1 => 3 k2 => 3 k3 => 3]␤» 
say @a-cloned# OUTPUT: «[k1 => 1 k2 => 2 k3 => 3]␤»

State variables are shared between all threads. The result can be unexpected.

sub code(){ state $i = 0say ++$i$i };
await
    start { loop { last if code() >= 5 } },
    start { loop { last if code() >= 5 } };
 
# OUTPUT: «1␤2␤3␤4␤4␤3␤5␤» 
# OUTPUT: «2␤1␤3␤4␤5␤» 
# many other more or less odd variations can be produced

The $ variable

In addition to explicitly declared named state variables, $ can be used as an anonymous state variable without an explicit state declaration.

say "1-a 2-b 3-c".subst(:g, /\d/{<one two three>[$++]});
# OUTPUT: «one-a two-b three-c␤»

Furthermore, state variables can be used outside of subroutines. You could, for example, use $ in a one-liner to number the lines in a file.

perl6 -ne 'say ++$ ~ " $_"' example.txt

Each reference to $ within a lexical scope is in effect a separate variable.

perl6 -e '{ say ++$; say $++  } for ^5'
# OUTPUT: «1␤0␤2␤1␤3␤2␤4␤3␤5␤4␤» 

That is why, if you need to reference the same $ variable (or, for that matter, any of the other anon state variables @ and %) more than once, a possible solution is to bind another variable to it, although in this example it would be more straightforward to just declare state $x and not use the magical/anonymous $ variable:

sub foo () {
    my $x := $;
    $x++;
    say $x;
    $x = $x + 1;
}
 
foo() for ^3# OUTPUT: «1␤3␤5␤» 

In general, it is better style to declare a named state variable in case you have to refer to it several times.

Note that the implicit state declarator is only applied to the variable itself, not the expression that may contain an initializer. If the initializer has to be called exactly once, the state declarator has to be provided.

for ^3 {       $ = .say } # OUTPUT: «0␤1␤2␤» 
for ^3 { state $ = .say } # OUTPUT: «0␤» 

The @ variable

Similar to the $ variable, there is also a Positional anonymous state variable @.

sub foo($x{
    say (@).push($x);
}
 
foo($_for ^3;
 
# OUTPUT: «[0] 
#          [0 1] 
#          [0 1 2]␤»

The @ here is parenthesized in order to disambiguate the expression from a class member variable named @.push. Indexed access doesn't require this disambiguation but you will need to copy the value in order to do anything useful with it.

sub foo($x{
    my $v = @;
    $v[$x= $x;
    say $v;
}
 
foo($_for ^3;
 
# OUTPUT: «[0] 
#          [0 1] 
#          [0 1 2]␤»

As with $, each mention of @ in a scope introduces a new anonymous array.

The % variable

In addition, there's an Associative anonymous state variable %.

sub foo($x{
    say (%).push($x => $x);
}
 
foo($_for ^3;
 
# OUTPUT: «{0 => 0} 
#          {0 => 0, 1 => 1} 
#          {0 => 0, 1 => 1, 2 => 2}␤»

The same caveat about disambiguation applies. As you may expect, indexed access is also possible (with copying to make it useful).

sub foo($x{
    my $v = %;
    $v{$x} = $x;
    say $v;
}
 
foo($_for ^3;
 
# OUTPUT: «{0 => 0} 
#          {0 => 0, 1 => 1} 
#          {0 => 0, 1 => 1, 2 => 2}␤»

As with the other anonymous state variables, each mention of % within a given scope will effectively introduce a separate variable.