【发布时间】:2020-06-01 11:48:24
【问题描述】:
我的问题与 require 在与所需命名空间的静态或动态分辨率一起使用时的行为有关。
我会尝试表达我对事物的理解:
[ 1 ] 将“require”与文字一起使用
{ require MODULE; }
在这种情况下,编译器会检查 MODULE 是否已被声明为符号。 如果没有,编译器会声明它,并将它绑定到一个空的占位符包,它只是为这个“require”创建的
{
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”的行为 NOT 作为“正常”子例程。
当我们将“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)它会引发异常。
因此我得出结论,也许“require”首先会检查与提供的字符串同名的命名空间。
顺便说一下,检查一下,我偶然发现了一个奇怪的行为。如下:
如果我们使用“use 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
这是我遗漏的错误还是其他什么?
【问题讨论】:
-
我没有读过你的问题,但你读过stackoverflow.com/questions/56089611/…吗?你的问题本质上是重复的吗?
-
是的,我有!但是我的问题与使用一种或另一种方式所产生的影响无关(至少乍一看不是)。我了解两种方式(静态和动态)的使用。它更多地是关于“if”和“how”,“require”语句不是一个普通的子程序,而是动态查找的转换代理。这只是确认我对事物的理解的问题。
-
好的。明白了。 :) 但是您在问题中的示例仍然是错误的。当我解决问题时,我做的第一件事就是加载示例代码并运行它。如果它没有按照提问者所说的那样做,那就令人困惑了。您的代码反映了您的困惑。如果您写了
{ my $foo = ::('EXAMPLE'); try require EXAMPLE },您可能会对此有所了解。同时,请考虑修改您的示例代码,或者更确切地说是 cmets,以对读者有意义。 TIA。 -
"虽然
require的执行顺序在查找之后"。这是不正确的。代码的执行顺序不一定遵循人类的阅读顺序。一般来说,Raku 中的所有代码都受到两个阶段和相移的影响。作为一个具体点,require是一个语句,它的一些行为在整个编译阶段执行,首先出现,一些在整体运行阶段执行,后面出现。require的编译阶段方面的执行发生在 查找的运行阶段方面的执行之前。 -
如果您编写了
BEGIN { my $response = ::('MODULE'); say $response.^name },那么您将相移大括号中的代码以在编译阶段执行,它会在之前执行@的编译阶段方面987654338@,所以它会显示Failure。 “模块不存在,因此查找导致编译阶段占位符包:模块”我认为这反映了您的困惑。 MODULE 确实 存在于您的示例代码中。您的say...在require MODULE执行其编译阶段行为之后执行,其中一部分是声明MODULE符号。
标签: require lookup raku rakudo