是否可以在运行时内省标量的范围?

cod*_*ons 7 introspection rakudo raku

如果我有以下变量

my $a    = 0;
my $*b   = 1;
state $c = 2;
our $d   = 3;
Run Code Online (Sandbox Code Playgroud)

我可以很容易地确定这$*b是动态的,但$a不是下面的代码

say $a.VAR.dynamic;
say $*b.VAR.dynamic;
Run Code Online (Sandbox Code Playgroud)

有什么方法可以类似地确定它$c是状态变量$d还是包范围的变量?(我知道我可以用will每个变量声明的特征来做到这一点,但我希望有一种不需要注释每个声明的方法。也许有 ::(...) 插值?)

jjm*_*elo 6

在包范围变量的情况下,不要太难:

our $foo = 'bar';
say $foo.VAR.name ? OUR::.keys
Run Code Online (Sandbox Code Playgroud)

我们在哪里使用OUR伪包。但是,没有STATE伪包这样的东西。它们显然出现在LEXICAL伪包中,但我找不到检查它们是否是状态变量的方法。对不起。


Eli*_*sen 5

据我所知,没有办法识别state变量。像任何词法一样,它存在于 lexpad 中。唯一不同的是,它有效地生成了第一次进入范围时进行初始化的代码。


cod*_*ons 3

正如 Elizabeth Mattijsen 正确指出的那样,目前无法state在运行时查看变量是否是变量。...至少在运行时技术上是这样

然而,正如 Jonathan Worthington 的评论所暗示的那样,可以在编译时检查这一点。而且,如果没有深层元编程恶作剧,变量是否是变量state在编译后是不可变的。当然,可以在编译时记下一些信息,然后在运行时使用它。

因此,可以在运行时知道变量是否是state具有以下几行(编译时)代码的变量,它提供了一种列出函数中list-state-vars所有变量的特征:state

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});
    }
};
Run Code Online (Sandbox Code Playgroud)

这段代码显然非常脆弱/依赖于 QAST 的 Rakudo 实现细节。希望使用 RAST 会更容易,但这种基本方法已经可行,同时,本QAST 黑客指南对于此类元编程来说是有用的资源。