今天,我们将使用 GitHub 上提供的 Rakudo 源代码挖掘 Bool 类型的内部。
Raku 是用 Raku 和 NQP(Not Quite Raku)语言编写的,这使得阅读源代码变得相对容易。当然,有很多东西不容易理解,或者没有反映在 Raku 语言的公开文档中。到目前为止,您都无法在 Raku 书籍中找到深入的细节。无论如何,对 Raku 有一些中级的理解,这仍然是可能的。
好的,回到 src/core/Bool.pm
文件。它以一些 BEGIN phasers 开始,它为 Bool 类添加了一些方法和 multi 方法。下一次我们将讨论元模型和类构造的细节。今天,对我们来说更有趣的是 Bool 类的方法在做什么。
gist 和 perl
gist
和 perl
方法返回对象的字符串表示形式:当变量被字符串化时隐式调用 gist
,perl
应该直接调用。它适用于 Raku 中的任何对象,但这种行为当然应该定义在某处。他们在这里:
Bool.^add_method('gist', my proto method gist(|) {*});
Bool.^add_multi_method('gist', my multi method gist(Bool:D:) {
self ?? 'True' !! 'False'
});
Bool.^add_multi_method('gist', my multi method gist(Bool:U:) {
'(Bool)'
});
Bool.^add_method('perl', my proto method perl(|) {*});
Bool.^add_multi_method('perl', my multi method perl(Bool:D:) {
self ?? 'Bool::True' !! 'Bool::False'
});
Bool.^add_multi_method('perl', my multi method perl(Bool:U:) {
'Bool'
});
试试以下简单程序中的方法:
my Bool $b = True;
say $b; # True
say "[$b]"; # [True]
$b.perl.say; # Bool::True
正如你所看到的,True
字符串由 gist
方法返回,而 perl
方法返回 Bool::True
。
这两种方法都是 multi 方法,在上面的例子中,使用了定义了参数的版本。如果您查看签名,您将看到方法指定参数的方式不同:Bool:D:
或Bool:U:
。字母 D 和 U 分别代表定义(defined)和未定义(undefined)。第一个冒号为该类型添加了一个属性,而第二个冒号表明该参数实际上是一个调用者。
因此,不同版本的方法会根据它们是在定义的还是未定义的布尔变量上调用而被触发。为了演示其他两种变体的行为,只需从代码中删除初始化程序部分即可
my Bool $b;
say $b; # (Bool)
$b.perl.say; # Bool
由于变量 $b
有一个类型,Raku 知道它应该调用方法的对象的类型。然后将它分派给带有 (Bool:U:)
签名的版本,因为该变量尚未定义。
例如,当一个未定义的变量出现在字符串中时,例如 say "[$b]"
,则不会调用 gist 方法。相反,您会收到错误消息。
Use of uninitialized value $b of type Bool in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
in block at bool-2.pl line 3
[]
错误消息说 Perl 知道变量是什么类型,但拒绝调用字符串化方法。
今天就到这儿了。下一次,我们将看看为 Bool
数据类型定义的其他方法。