注意 “模块” 在 Raku 中是一个重载的术语;本文档重点介绍 module 声明符的使用。

什么是模块?§

模块,就像类和语法一样,是一种 。模块对象是 ModuleHOW 元类的实例;这提供了一些功能,这些功能对于创建命名空间、版本控制、委托和数据封装很有用(另请参见 角色)。

要创建模块,请使用 module 声明符

module M {}
say M.HOW;   # OUTPUT: «Perl6::Metamodel::ModuleHOW.new␤»

这里我们定义了一个名为 M 的新模块;使用 HOW 进行的内省确认 M 底层的元类是 Perl6::Metamodel::ModuleHOW

何时使用模块§

模块主要用于封装不属于类或角色定义的代码和数据。模块内容(类、子例程、变量等)可以使用 `is export` 特性从模块中导出;这些内容在模块使用 `import` 或 `use` 导入后,将在调用者的命名空间中可用。模块还可以通过 `our` 选择性地公开其命名空间内的符号,以便进行限定引用。

使用模块§

为了说明模块作用域和导出规则,让我们从定义一个简单的模块 `M` 开始

module M {
  sub greeting ($name = 'Camelia'{ "Greetings, $name!" }
  our sub loud-greeting (--> Str)  { greeting().uc       }
  sub friendly-greeting is export  { greeting('friend')  }
}

回想一下,子例程在词法上是作用域的,除非另有说明(声明符 sub 等同于 `my sub`),因此上面的例子中的 `greeting` 在词法上是作用域到模块的,在模块外部无法访问。我们还使用 `our` 声明符定义了 `loud-greeting`,这意味着除了词法作用域之外,它还在模块的符号表中被别名化。最后,`friendly-greeting` 被标记为导出;当模块被导入时,它将在调用者的符号表中注册。

import M;               # import the module 
say M::loud-greeting;   # OUTPUT: «GREETINGS, CAMELIA!␤» 
say friendly-greeting;  # OUTPUT: «Greetings, friend!␤» 

磁盘上的模块§

虽然 `.raku` 和 `.rakumod` 文件有时被称为“模块”,但它们实际上只是普通文件,当你写 `need`、`use` 或 `require` 时,它们会被加载和编译。

为了让 `.rakumod` 文件提供一个我们一直在使用的模块,它需要使用上面文档中的 `module` 声明一个模块。例如,通过将模块 `M` 放置在 `Foo.rakumod` 中,我们可以加载并使用该模块,如下所示

use Foo;                # find Foo.rakumod, run need followed by import 
say M::loud-greeting;   # OUTPUT: «GREETINGS, CAMELIA!␤» 
say friendly-greeting;  # OUTPUT: «Greetings, friend!␤» 

注意文件和模块名称之间的解耦——一个 `.rakumod` 文件可以声明零个或多个具有任意标识符的模块。

文件和模块命名§

通常我们希望一个 `.rakumod` 文件提供一个单一的模块,而没有其他东西。这里一个常见的约定是让文件基名与模块名匹配。回到 `Foo.rakumod`,很明显它只提供了一个模块 `M`;在这种情况下,我们可能希望将 `M` 重命名为 `Foo`。修改后的文件将读取

module Foo {
  sub greeting ($name = 'Camelia'{ "Greetings, $name!" }
  our sub loud-greeting (--> Str)  { greeting().uc       }
  sub friendly-greeting is export  { greeting('friend')  }
}

这可以被调用者更一致地使用(注意 `use Foo` 和 `Foo::` 之间的关系)。

use Foo;
say Foo::loud-greeting;  # OUTPUT: «GREETINGS, CAMELIA!␤» 
say friendly-greeting;   # OUTPUT: «Greetings, friend!␤» 

如果 `Foo.rakumod` 被放置在源代码树的更深处,例如在 `lib/Utils/Foo.rakumod` 中,我们可以选择将模块命名为 `Utils::Foo` 以保持一致性。

`unit` 关键字§

只提供单个模块的文件可以使用 `unit` 关键字更简洁地编写;`unit module` 指定编译单元的其余部分是声明的模块的一部分。以下是使用 `unit` 重写的 `Foo.rakumod`

unit module Foo;
 
sub greeting ($name = 'Camelia'{ "Greetings, $name!" }
our sub loud-greeting (--> Str)  { greeting().uc       }
sub friendly-greeting is export  { greeting('friend')  }

单元声明之后的所有内容都是 `Foo` 模块规范的一部分。

(注意 `unit` 也可以与 `class`、`grammar` 和 `role` 一起使用。)

如果我省略了 `module` 会发生什么?§

为了更好地理解 `module` 声明符在 `Foo.rakumod` 中的作用,让我们将其与一个省略了声明的变体文件 `Bar.rakumod` 进行对比。下面的子例程定义几乎相同(唯一的区别在于 `greeting` 的主体,为了清晰起见进行了修改)。

sub greeting ($name = 'Camelia'{ "Greetings from Bar, $name!" }
our sub loud-greeting (--> Str)  { greeting().uc                }
sub friendly-greeting is export  { greeting('friend')           }

作为提醒,以下是我们之前如何使用 `Foo.rakumod` 的,

use Foo;
say Foo::loud-greeting;  # OUTPUT: «GREETINGS, CAMELIA!␤» 
say friendly-greeting;   # OUTPUT: «Greetings, friend!␤» 

以下是我们如何使用 `Bar.rakumod` 的,

use Bar;
say loud-greeting;       # OUTPUT: «GREETINGS FROM BAR, CAMELIA!␤» 
say friendly-greeting;   # OUTPUT: «Greetings from Bar, friend!␤» 

注意使用了 `loud-greeting` 而不是 `Bar::loud-greeting`,因为 `Bar` 不是一个已知的符号(我们在 `Bar.rakumod` 中没有创建名为 `Bar` 的 `module`)。但是为什么 `loud-greeting` 可以调用,即使我们没有标记它为导出?答案很简单,`Bar.rakumod` 不会创建一个新的包命名空间——`$?PACKAGE` 仍然设置为 `GLOBAL`——因此当我们声明 `loud-greeting` 为 `our` 时,它将在 `GLOBAL` 符号表中注册。

词法别名和安全性§

值得庆幸的是,Raku 保护我们不会意外地覆盖调用站点定义(例如内置函数)。考虑以下对 `Bar.rakumod` 的添加

our sub say ($ignored{ print "oh dear\n" }

这将创建一个词法别名,隐藏了 `Bar.rakumod` 内部的 `say` 内置函数,但保持调用者的 `say` 不变。因此,以下对 `say` 的调用仍然按预期工作

use Bar;
say 'Carry on, carry on...';  # OUTPUT: «Carry on, carry on...␤»