我的问题与require与所需命名空间的静态或动态分辨率一起使用时的行为有关。
我将尝试表达我对事物的理解:
[ 1 ] 将“require”与文字一起使用
{ require MODULE; }
在这种情况下,编译器会检查 MODULE 是否已被声明为符号。如果没有,编译器声明它,并将它绑定到一个空的占位符包,它只是为这个“要求”创建的
{
my $response = ::('MODULE'); # this happens at runtime
say $response.^name; # MODULE doesn't exist so the lookup results in the compilation-phase placeholder package: MODULE
try require MODULE; # although the execution order of require comes after the lookup,
# the placeholder package creation was done during compilation and the package is present in the current scope during run-time
}
[ 2 ] 将“require”与字符串一起使用
{ try require 'FILE_PATH'; }
在这种情况下,“require”试图(在运行时)查找由字符串中声明的文件名定义的文件。如果找到(具有适当的内容:模块、包等),则它会在当前范围内创建一个名称空间,并将其与文件的内容一起加载。
[ 3 ] 在动态查找中使用“require”
{ try require ::('MODULE'); }
在我看来,在这种情况下,“require”的行为不是“正常”的子程序。
当我们将“require”与“动态查找”一起使用时,动态查找的核心功能将“融合”在一个新的例程中,该例程的行为与我们预期的不同。
事实是“动态查找”例程的结果不是符号就是失败。
如果“require”的行为类似于“正常”子例程,那么它可以使用的唯一输入将是其后的动态查找的结果(命名空间或失败)。
但事实上,在失败的情况下(作为动态查找的结果),“require”会继续在存储库中搜索适当的包(通常情况下,使用我们为动态查找提供的参数: '模块')。
因此,从这个意义上说,显然“要求”的行为不像“正常”子程序。
根据我的思路,require + 动态查找的组合类似于以下构造:
{ modified_dynamic_lookup('MODULE') :if_symbol_not_found_search_repositories_and_if_appropriate_package_found_create_namespace_and_load_package_contents; }
我关心的是我对案例[3]的理解。
require + 动态查找如何工作?(从分析上讲 - 编译器首先遵循的步骤是什么,然后是运行时?)
[ 后记 ]
我同意@raiph 的观点,“require”不是一个子例程,它与语言深度集成。
从这个意义上说,在 require “指令”之后的“动态查找结构”用于两件事:
通知编译器构造是“动态的”(所以不要在编译时修复任何东西)
提供将用于搜索符号、命名空间、文件或存储库内容的字符串
@raiph 表示他认为“require”在成功加载后会进行查找。
我对此的唯一反对意见是,当我们加载同一个库时,“require”不会引发任何异常。
它是否默默地忽略了加载的库?当它可以首先检查同一个命名空间是否已经在使用时,为什么还要费心做这么多工作呢?
相反,当我们假设我们加载了一个不同的库时,它会抛出一个异常:正在使用的符号的“重复定义”。
为了证明我进行了以下操作:
在 ./lib 目录中,我放置了两个库,“foo.pm6”是“foo”的单元定义,其中定义了 A 类:
file "foo.pm6" contents:
-----------------------------------
unit module foo;
class A is export {}
和另一个库“other.pm6”,这次在“foo”的定义中定义了一个不同的类 B。
file "other.pm6" contents:
-----------------------------------
module foo {
class B is export {}
}
raku 程序文件包含以下内容:
use lib <lib>;
my $name = 'other'; # select one of {'other', 'foo'}
require ::('foo') <A>; ########> Initial package loading
my $a = try ::('foo::A').new;
say '(1) ' ~ $a.^name; # (1) foo::A
$a = ::('A').new;
say '(2) ' ~ $a.^name; # (2) foo::A
try require ::($name); # if $name eq 'other' => throws exception, if $name eq 'foo' => does nothing
with $! {.say}; # P6M Merging GLOBAL symbols failed: duplicate definition of symbol foo ...
$a = try ::('foo::A').new;
say '(3) ' ~ $a.^name; # (3) foo::A
$a = ::('A').new;
say '(4) ' ~ $a.^name; # (4) foo::A
从上面的示例中我们看到,当我们尝试重新加载 foo 命名空间时,它隐藏在具有不同名称的文件中(只是为了欺骗 raku),它会引发异常。
因此我得出结论,也许“需要”首先检查与提供的字符串同名的命名空间。
顺便说一句,检查一下,我偶然发现了一个奇怪的行为。如下:
如果我们使用“使用 foo;” 在 line: "Initial package loading" 而不是 "require ::('foo') ;" 中,我们得到以下结果:
(1) foo::A
(2) foo::A
No such symbol 'other' ...
(3) Any
(4) foo::A
在 (3) 中查找 'foo::A' 没有找到任何东西!
此外,如果我使用以下内容更改库文件:“other.pm6”(A 类而不是 B - 如 foo.pm6 中)
file "other.pm6" contents:
-----------------------------------
module foo {
class A is export {}
}
结果似乎恢复到预期:
(1) foo::A
(2) foo::A
No such symbol 'other' ...
(3) foo::A
(4) foo::A
是错误还是我缺少的其他东西?