包是命名程序元素的嵌套命名空间。 模块、类、语法等都是包的类型。就像目录中的文件一样,如果命名元素是本地的,你通常可以使用它们的短名称来引用它们,或者如果它们的范围允许,可以使用包含命名空间的较长名称来消除歧义。

名称§

名称是任何可以作为变量名称一部分的合法内容(不包括 sigil)。这包括

class Foo {
    sub zape () { say "zipi" }
    class Bar {
        method baz () { return 'Þor is mighty' }
        our &zape = { "zipi" };
        our $quux = 42;
    }
}
 
my $foo;                # simple identifiers 
say Foo::Bar.baz;       # calling a method; OUTPUT: «Þor is mighty␤» 
say Foo::Bar::zape;     # compound identifiers separated by ::; OUTPUT: «zipi␤» 
my $bar = 'Bar';
say $Foo::($bar)::quux# compound identifiers with interpolations; OUTPUT: «42␤» 
$42;                    # numeric names 
$!;                     # certain punctuation variables 

:: 用于分隔嵌套的包名称。

包本身并没有真正的标识;它们可以仅仅是模块或类名的一部分,例如。它们更类似于命名空间而不是模块。如果存在同名模块,它将捕获包的标识。

package Foo:ver<0> {};
module Foo:ver<1> {};
say Foo.^ver# OUTPUT: «1␤» 

语法允许声明的包使用版本以及授权 auth,但实际上,它被忽略了;只有模块、类和其他更高阶类型对象具有可能包含 authver 的标识。

package Foo:ver<0>:auth<bar> {};
say Foo.^auth;
# OUTPUT: «(exit code 1) No such method 'auth' for invocant of type 
# 'Perl6::Metamodel::PackageHOW' ... 

包限定名§

普通的包限定名看起来像这样:$Foo::Bar::quux,它将是包 Foo::Bar 中的 $quux 变量;Foo::Bar::zape 将表示同一包中的 &zape 变量。

有时将 sigil 与变量名放在一起更清晰,因此另一种写法是

Foo::Bar::<$quux>

这对于 Foo«&zape» 变量不起作用,因为 sub 默认情况下具有词法作用域。名称在编译时解析,因为变量名是常量。我们可以访问 Bar 中的其余变量(如上面的示例所示),因为类默认情况下具有包作用域。

如果 :: 之前的名称部分为空,则表示包未指定,必须搜索。通常这意味着在主 sigil 之后紧跟的初始 :: 对编译时已知的名称是无操作的,尽管 ::() 也可用于引入插值。此外,在没有其他 sigil 的情况下,:: 可以用作它自己的 sigil,表示有意使用尚未声明的包名。

伪包§

以下伪包名称在名称的开头是保留的

MY当前词法作用域中的符号(又名 $?SCOPE)
OUR当前包中的符号(又名 $?PACKAGE)
CORE最外层词法作用域,标准 Raku 的定义
GLOBAL解释器范围的包符号,实际上是 UNIT::GLOBAL
PROCESS与进程相关的全局变量(超级全局变量)。动态变量查找将查找的最后一个位置。
COMPILING正在编译的作用域中的词法符号

以下相对名称也是保留的,但可以在名称中的任何位置使用

CALLER直接调用者的词法作用域中的动态符号
CALLERS任何调用者的词法作用域中的动态符号
DYNAMIC我或任何调用者的词法作用域中的动态符号
OUTER下一个外层词法作用域中的符号
OUTERS任何外层词法作用域中的符号
LEXICAL我或任何外层词法作用域中的动态符号
UNIT编译单元的最外层词法作用域中的符号
SETTING单元的 DSL 中的词法符号(通常是 CORE)
PARENT此包的父包(或词法作用域)中的符号
CLIENT来自不同包的最近 CALLER

文件的范围称为 UNIT,但其外部存在一个或多个词法作用域,对应于语言设置(在其他文化中通常称为序言)。因此,SETTING 作用域等效于 UNIT::OUTERS。对于标准的 Raku 程序,SETTINGCORE 相同,但各种启动选项(如 -n-p)可以将您置于特定领域语言中,在这种情况下,CORE 仍然是标准语言的作用域,而 SETTING 代表定义 DSL 的作用域,该 DSL 充当当前文件的设置。当用作名称中间的搜索词时,SETTING 包括其所有外层作用域,直到 CORE。要仅获取设置的最外层作用域,请使用 UNIT::OUTER 代替。

查找名称§

插值到名称§

你可以使用 字符串插值 将一个字符串插值到包名或变量名中,使用 ::($expr),在通常放置包名或变量名的地方。该字符串允许包含额外的 :: 实例,这些实例将被解释为包嵌套。你只能插值整个名称,因为该结构以 :: 开头,并且要么立即结束,要么以括号外的另一个 :: 继续。大多数符号引用都是使用这种表示法完成的

my $foo = "Foo";
my $bar = "Bar";
my $foobar = "Foo::Bar";
$::($bar)              # lexically-scoped $Bar 
$::("MY::$bar")        # lexically-scoped $Bar 
$::("OUR::$bar")       # package-scoped $Bar 
$::("GLOBAL::$bar")    # global $Bar 
$::("PROCESS::$bar")   # process $Bar 
$::("PARENT::$bar")    # current package's parent's $Bar 
$::($foobar)           # $Foo::Bar 
@::($foobar)::baz      # @Foo::Bar::baz 
@::($foo)::Bar::baz    # @Foo::Bar::baz 
@::($foobar)baz        # ILLEGAL at compile time (no operator baz) 
@::($foo)::($bar)::baz # @Foo::Bar::baz 

初始的 :: 并不意味着全局;这里作为插值语法的一部分,它甚至不意味着包。在插值 ::() 组件之后,间接名称的查找方式与在原始源代码中存在时完全相同,优先级首先给领先的伪包名,然后给词法范围内的名称(向外搜索范围,以 CORE 结束)。当前包最后被搜索。

使用 MY 伪包将查找限制在当前词法范围,使用 OUR 将范围限制在当前包范围。

同样,类名和方法名也可以插值

role with-method {
    method a-method { return 'in-a-method of ' ~ $?CLASS.^name  };
}
 
class a-class does with-method {
    method another-method { return 'in-another-method' };
}
 
class b-class does with-method {};
 
my $what-class = 'a-class';
 
say ::($what-class).a-method# OUTPUT: «in-a-method of a-class␤» 
$what-class = 'b-class';
say ::($what-class).a-method# OUTPUT: «in-a-method of b-class␤» 
 
my $what-method = 'a-method';
say a-class."$what-method"(); # OUTPUT: «in-a-method of a-class␤» 
$what-method = 'another-method';
say a-class."$what-method"(); # OUTPUT: «in-another-method␤»

直接查找§

要在不扫描的情况下直接在包的符号表中查找,将包名视为哈希

Foo::Bar::{'&baz'}  # same as &Foo::Bar::baz 
PROCESS::<$IN>      # same as $*IN 
Foo::<::Bar><::Baz> # same as Foo::Bar::Baz 

::() 符号引用不同,这不会解析 :: 的参数,也不会从该初始点开始启动命名空间扫描。此外,对于常量下标,它保证在编译时解析符号。

你不能在正则表达式中使用这种直接查找,除非你发出 MONKEY-SEE-NO-EVAL 编译指示。此措施的主要目的是避免用户输入在外部执行。

空伪包与普通名称搜索的搜索列表相同。也就是说,以下所有含义都相同

$foo
::{'$foo'}
::<$foo>

它们都向外扫描词法范围,然后是当前包范围(尽管当“严格”生效时,包范围随后被禁止)。

包查找§

将包对象本身作为哈希对象进行下标,其键是变量名,包括任何符号。包对象可以通过使用 :: 后缀从类型名派生

MyType::<$foo>

类成员查找§

方法(包括自动生成的方法,例如公共属性的访问器)存储在类元对象中,可以通过 lookup 方法查找。

Str.^lookup('chars')

全局变量§

解释器全局变量驻留在 GLOBAL 包中。用户的程序从 GLOBAL 包开始,因此主线代码中的“our”声明默认情况下进入该包。进程范围变量驻留在 PROCESS 包中。大多数预定义的全局变量,如 $*UID$*PID,实际上是进程全局变量。

模块的编程使用§

有时能够以编程方式定义和使用模块非常有用。这是一个实际的例子。

假设我们有一系列模块,其模块文件位于这样的目录树中

lib/
    TomtomMaps/
        Example/
            # a directory of example file modules programmatically
            # created from the Tomtom Maps SDK
            A01.rakumod
            #...
            C09.rakumod
            #...

模块 TomtomMaps::Example::C09 看起来像这样

unit module TomtomMaps::Example::C09;
 
# ensure you use the 'our' declarator 
our sub print-example($fh# filehandle open for writing 
                      :$api-maps-key
{ # do not need to export the sub 
    $fh.print: qq:to/HERE/; 
    # ... the example html file
    HERE
}

我们可以像这样访问和使用子例程

use lib 'lib';
my $module-dir = 'TomtomMaps::Example';
my $example    = 'C09';
my $m          = "{$module-dir}::{$example}";
 
# you must use the runtime 'require', not the compile-time 'use' 
require ::($m);
 
my $fh = open "./example-{$example}.html":w;
my $api-maps-key = 'ghghfxnnhrgfsWE.mmn';
 
# execute the subroutine 
&::($m)::print-example($fh:$api-maps-key); # the map's html file is written 
 
$fh.close;