Is it possible to introspect into the scope of a Scalar at runtime?

Viewed 122

If I have the following variables

my $a    = 0;
my $*b   = 1;
state $c = 2;
our $d   = 3;

I can easily determine that $*b is dynamic but $a is not with the following code

say $a.VAR.dynamic;
say $*b.VAR.dynamic;

Is there any way to similarly determine that $c is a state variable and $d is a package-scoped variable? (I know that I could do so with a will trait on each variable declaration, but I'm hopping there is a way that doesn't require annotating every declaration. Maybe something with ::(...) interpolation?)

3 Answers

In the case of the package-scoped variable, not too hard:

our $foo = 'bar';
say $foo.VAR.name ∈ OUR::.keys

where we're using the OUR pseudopackage. However, there's no such thing as a STATE pseudopackage. They obviously show up in the LEXICAL pseudopackage, but I can't find a way to check if they're a state variable or not. Sorry.

To my knowledge, there is no way to recognize a state variable. Like any lexical, it lives in the lexpad. The only thing different about it, is that it effectively has code generated to do the initialization the first time the scope is entered.

As Elizabeth Mattijsen correctly noted, it is currently not possible to see whether a variable is a state variable at run time. ... at least technically at runtime.

However, as Jonathan Worthington's comment implies, it is possible to check this at compile time. And, absent deep meta-programming shenanigans, whether a variable is a state variable is immutable after compile-time. And, of course, it's possible to make note of some info at compile time and then use it at runtime.

Thus, it's possible to know, at runtime, whether a variable is a state one with (compile-time) code along the following lines, which provides a list-state-vars trait that lists all the state variables in a function:

multi trait_mod:<is>(Sub \f, :$list-state-vars) {
    use nqp;
    given f.^attributes.first({.name eq '@!compstuff'}).get_value(f)[0] {
       say .list[0].list.grep({try .decl ~~ 'statevar'}).map({.name});
    }
};

This code is obviously pretty fragile/dependent on the Rakudo implementation details of QAST. Hopefully this will be much easier with RAST, but this basic approach is already workable and, in the meantime, this guide to QAST hacking is a helpful resource for this sort of meta programming.

Related