gpt4 book ai didi

require - 需求行为(静态+动态)[RAKU]

转载 作者:行者123 更新时间:2023-12-03 14:14:52 24 4
gpt4 key购买 nike

我的问题与与所需 namespace 的静态或动态解析一起使用时需要的行为有关。

我将尝试表达我对事物的理解:

[1]与文字一起使用“require”

    { require MODULE; }

在这种情况下,编译器将检查是否已将MODULE声明为符号。
如果还没有,编译器会声明它,并将其绑定(bind)到一个空的占位符包,该包是为此“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”试图(在运行时)查找由字符串中声明的文件名定义的文件。
如果找到(具有适当的内容:模块,软件包等),则它将在当前作用域中创建一个 namespace ,并将其与文件的内容一起加载。

[3]与动态查询一起使用“require”
    { try require ::('MODULE'); }

在我看来,在那种情况下,“require”行为 而不是作为“常规”子例程。

当我们将“要求”与“动态查找”一起使用时,动态查找的核心功能将在新例程中“融合”,该例程的行为与我们期望的有所不同。

事实是“动态查找”例程的结果是符号或失败。

如果“要求”的行为类似于“常规”子例程,则它可以使用的唯一输入将是紧随其后的动态查找( namespace 或失败)的结果。

但这也是一个事实,在失败的情况下(作为动态查找的结果),“require”继续在存储库中搜索适当的软件包(通常是这种情况,尽管如此,我们还是使用了动态查找的参数: '模块')。

因此,显然,“需求”在这种意义上并不像“正常”子程序那样。

根据我的想法,require +动态查找的组合类似于以下构造:
{ modified_dynamic_lookup('MODULE') :if_symbol_not_found_search_repositories_and_if_appropriate_package_found_create_namespace_and_load_package_contents; }

我关心的是我对案例的理解[3]。

+动态查询如何工作? (从分析上来说-编译器首先执行然后在运行时执行的步骤是什么?)

[Post Scriptum]

我同意@raiph的观点,“require”是 而不是子例程,它已深深地集成在该语言中。

从这个意义上讲,遵循要求“指令”的“动态查找结构”用于两件事:
  • 通知编译器该结构是“动态的”(因此请不要
    麻烦在编译时修复任何东西)
  • 要提供用于搜索符号的字符串,
    namespace ,文件或存储库内容

  • @raiph指出,他认为“require”在成功加载后会进行查找。

    我唯一的反对意见是,当我们加载相同的库时,“require”不会引发任何异常。

    是否默默地忽略了已加载的库?
    当首先检查相同的 namespace 已被使用时,为什么还要花那么多精力呢?

    相反,当我们假装加载另一个库时,它将引发Exception:正在使用的符号的“重复定义”。

    为了说明这一点,我进行了以下操作:

    在./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 namespace 时,该 namespace 隐藏在具有不同名称的文件中(只是为了欺骗raku),它将引发异常。

    因此,我得出结论,也许“需要”首先检查与提供的字符串具有相同名称的 namespace 。

    顺便提一下,我偶然发现了一个奇怪的行为。内容如下:

    如果我们使用“use foo;”在“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

    是我遗失的错误还是其他?

    最佳答案

    重写为与您的答案的the third version相对应。
    [1]在文字上使用“require”

    In this case the compiler checks to see if MODULE has already been declared as a symbol. If it hasn't, the compiler declares it, and binds it to an empty placeholder package it's just created for this "require"


    更具体地说, require关键字以及由它生成的代码4起作用。
    它创建符号的唯一原因是使人们可以编写该标识符,然后代码将被编译。如果 require不这样做,那么即使 require FOO成功,使用标识符的代码也将无法编译:
    require FOO;
    my FOO $bar; # Type 'FOO' is not declared

    # MODULE doesn't exist so the lookup results in the compilation-phase placeholder package: MODULE

    MODULE确实存在。并且查找成功。它返回绑定(bind)到 MODULE符号的值,该符号是 require在编译阶段放置在其中的占位符包。

    # although the execution order of require comes after the lookup

    require的编译阶段操作的执行发生在运行阶段之前的查找之前。
    [2]与字符串一起使用“require” **

    If found (with appropriate content: modules, packages etc.) then it creates a namespace(s) in the current scope and loads it with the content(s) of the file.


    我认为符号 require的唯一声明是代码编写者已将其明确写为 require语句的一部分的静态标识符的声明。例子:
  • require MODULE <A>;-> MODULEA
  • require 'MODULE.pm6' <A>;-> A
  • require ::('MODULE') <A>;-> A

  • 作为符号合并(P6M)的一部分,MLS1会声明其他必要的符号。但是 require不能完成这项工作。它由MLS代表其完成。而且它不是 require所特有的。这是由于 use语句在编译阶段进行的相同(某种工作)。
    [3]使用“require”进行动态查找
    { try require ::('MODULE'); }
    我有一些代码试图证明在加载模块之前并没有进行查找。2

    It seems to me that in that case "require" behaves NOT as a "normal" subroutine.

    require不是常规例程,否则不是常规例程。
    say require MODULE;   # Undeclared name:
    MODULE used at line 1
    # Undeclared routine:
    require used at line 1
    如果在 the official doc中搜索 require,您会发现它未在 例程引用部分中列出,而是在 语言引用的模块部分中列出。它是关键字,语句,是编译器可以理解的语言的特殊部分。

    If "require" behaves like a "normal" subroutine, then the only input it could use, would be the result of the dynamic lookup that followed it (Namespace or Failure).


    动态查找的结果是绑定(bind)到 Symbol的值(如果已声明),否则是 Failure:
    my $variable = 42;
    say ::('$variable'); # 42
    say ::('nonsense') ~~ Failure; # True
    $variable不是命名空间。

    But it is also a fact that in the case of a Failure (as the result of dynamic lookup), "require" continues searching the repositories for a proper package (as is normally the case, using nevertheless the argument we gave to dynamic lookup: 'MODULE').


    给定我编写的跟踪动态查找:::''MODULE')2值的代码,在我看来,如果模块加载失败,则无论是 require还是MLS,任何代码都不会对其进行动态查找。
    反过来,这意味着它仅在(成功)加载模块期间或之后(如果有的话)发生。因此,要么是MLS正在执行(似乎很有可能),要么是成功地加载了模块之后 require正在这样做(似乎不太可能,但我还没有准备100%消除它)。 { modified_dynamic_lookup('MODULE') :if_symbol_not_found_search_repositories_and_if_appropriate_package_found_create_namespace_and_load_package_contents; }我想我已经证明 require或MLS根本没有查找任何内容,或者只有通过模块成功加载才可以进行查找。

    what are the steps followed by the compiler at first and then by the runtime?


    这个答案当然是为了回答这个问题,但是我的简要编译器代码分析可能会有所帮助。3(尽管单击链接以查看 Actions.nqp中的实际代码并不适合胆小的人!)
    [圣经经文]

    In that sense the "dynamic lookup construct" that follows the require "instruction" is used for 2 things:

    1. To notify the compiler that the construct is "dynamic" (so don't bother fixing anything at compile time)

    2. To provide the string that will be used to search for symbols, namespaces, files or repository content


    我认为它只能执行2,只是传递给MLS的程序包名称。

    when we load the same library "require" doesn't throw any exception. Is it silently ignoring the loaded library?


    我不认为 require对此一无所知。它将其交给MLS,然后在MLS完成其任务后再接听。我认为 require不能告诉您何时MLS成功执行了新的加载与何时仅跳过了加载之间的区别。它所知道的只是MLS说一切都好还是有异常(exception)。

    Why bother doing so much work when it can check first that the same namespace is already in use?


    为什么在MLS已经完成并且 require仍然要调用MLS的情况下进行任何工作呢?做任何事情都是浪费精力。 require要做的就是处理用户在 require语句中明确键入的编译阶段符号。它不能要求MLS进行处理,因为它与成功的模块加载无关,而这是MLS摆弄符号的唯一情形。

    In contrary when we pretend that we load a different library then it throws an Exception : "duplicate definition" of the symbol in use.


    试试这个:
    require ::('foo');
    require ::('other');
    现在,当您将 unit module foo;foo.pm6中的 other.pm6更改为 unit module bar;时,再试一次。您仍然会遇到相同的异常,但是符号将是 barrequire如何知道 bar?不可以异常来自MLS,并且该符号仅由MLS知道。

    Therefore I conclude that maybe "require" checks first for a namespace that has the same name as the provided string.


    除非您将MLS视为 require的一部分,否则我相信您现在可以看到您的“也许”资格是明智的。 :)

    I stumbled upon a strange behaviour ... The lookup of 'foo::A' in (3) doesn't find anything !!!


    我对此有一个解释。我并不是说这是对的,但是当我这样写的时候,这对我来说似乎并不奇怪: use语句加载 foo.pm6包。它定义了一个 foo包,其中包含一个 A类,并导出 A。这会导致在导入的词法作用域 foo中产生一个符号,该符号绑定(bind)到一个包,该包包含一个符号 A。它还会在导入的词法范围内产生另一个符号 Arequire语句加载 other.pm6包。它定义了一个 foo包,其中包含一个 B类,并导出 B。这导致将导入词法作用域中的 foo符号重新绑定(bind)到另一个包,即包含符号 B的新包。它还会在导入的词法范围内产生另一个符号 B
    较早的 A徘徊。 (换句话说,P6M符号合并过程不包括删除符号。)但是在绑定(bind)到 foo::A符号的包中查找的 foo不再存在,因为绑定(bind)到 foo符号的包现在是从 other.pm6包,已覆盖了 foo.pm6包中的一个。
    同时,还有另一个奇怪之处:
    try require ::($name);
    with $! {.say}; # No such symbol 'other' ...
    我认为这反射(reflect)了 require在成功加载模块后执行了(失败的)查找。
    请注意,如果模块加载失败,则不会出现此消息。这似乎再次证实了我的想法(和代码2),直到成功加载后 require才进行任何查找(如果那样;我仍然对这是MLS还是 require并没有强烈的意识; code4对我来说太复杂了(atm)。
    对您的评论的回应
    从您对这个答案的评论:

    Its like we get as the result of the amalgamation of require + 'dynamic lookup formulation' an enhanced dynamic lookup like this { ::('something') :if_not_found_as_namespace_check_repositories_and_load }


    由于种种原因,这对我来说并不正确。
    例如,假设存在一个声明为 foomodule foo { our sub bar is export { say 99 } }软件包,如果 require d可以成功加载。现在考虑以下代码:
    my \foo = 42;
    say ::('foo'); # 42
    require ::('foo') <&bar>;
    say foo; # 42
    bar; # 99
    这对我来说很有意义。它不会加载名称为 42的软件包。它不会查找符号 foo。它将加载名称为 foo的软件包。尽管它可能会在加载程序包后查找 foo符号,但由于已经存在一个符号,所以它不会安装 foo符号。
    脚注
    1所谓模块加载子系统,是指给定模块名称的各个部分,它们执行诸如搜索本地文件系统或数据库,检查预编译目录,调用编译以及在模块成功加载时合并符号的操作。我不知道零件之间以及零件与编译器之间的边界在哪里。但是我相信它们不是 require的一部分,而只是被它调用。

    2运行以下代码:
    my \MODULE =
    { my $v;
    Proxy.new:
    FETCH => method { say "get name: $v"; $v },
    STORE => method ($n) { say "set name: $n"; $v = $n }}();

    MODULE = 'unseen by `require`';
    say ::('MODULE');

    use lib '.';
    say 'about to `require`';
    require ::('MODULE');

    3我们从 the relevant match in Raku's Grammar.nqp file开始:
      rule statement_control:sym<require> {
    <sym>
    [
    | <module_name>
    | <file=.variable>
    | <!sigil> <file=.term>
    ]
    <EXPR>?
    }
    该代码似乎遵循了我们的期望-一个 require关键字,后跟以下任意一个:
  • 包标识符(<module_name>);或者
  • 一个<variable>(例如$foo);或者
  • 不是以<term>开头的<sigil>

  • 我们对 <module_name>分支感兴趣。它调用 token module_name,调用 token longname,调用 token name:
    token name {
    [
    | <identifier> <morename>*
    | <morename>+
    ]
    }
    显然, ::('foo')并非以 <identifier>开头。所以是 token morename 。我将删去一些没意思的行:
        token morename {
    '::'
    [
    || <?before '(' | <.alpha> >
    [
    | <identifier>
    | :dba('indirect name') '(' ~ ')' [ <.ws> <EXPR> ]
    ]
    ]?
    }
    答对了。这将匹配 ::(,尤其是 :dba('indirect name') '(' ~ ')' [ <.ws> <EXPR> ]位。
    因此,在这一点上,我们将捕获:
    statement_control:sym<require><module_name><longname><name><morename><EXPR>
    不久之后, statement_control:sym<require> token 将成功。因此,这时它将在 Actions.nqp中调用相应的action方法。

    4在 Actions.nqp中,我们找到与 token statement_control:sym<require>相对应的 Action ,即 method statement_control:sym<require> 。条件的开头 if $<module_name> {将是 True,从而导致运行此代码:
    $longname := $*W.dissect_longname($<module_name><longname>);
    $target_package := $longname.name_past;
    在我看来,这段代码将剖析 ::('foo')的结果,并将与该剖析相对应的AST绑定(bind)到 $target_package,而不是费心进行查找或准备运行时查找。
    如果我是对的,则 ::('foo')不必超过 require可以解释的9个字符,但是它很可能会解释它们。这里没有必要的含义,因为它构造了程序包加载代码,因此它可以执行任何特定的操作,例如查找。

    Action 的后半部分会进行查找。有像 this这样的行:
    ?? self.make_indirect_lookup($longname.components())
    并赋予例程名称,我认为这是在进行查找,这可能是 require尝试在成功加载软件包时添加软件包符号的一部分。

    关于require - 需求行为(静态+动态)[RAKU],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62131486/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com