syntax - 访问、实例化和声明模块之间有什么区别?

标签 syntax module macros racket

Racket 中的函数如module->language-info , module->imports ,和module->exports期望它们的模块被声明,但不一定被访问或实例化。

现在,dynamic-require似乎有几个关于如何请求模块的选项,包括访问和实例化。

这让我想知道,声明模块、访问模块和实例化模块有什么区别?

最佳答案

声明一个模块只是该模块位于当前命名空间中的某个位置。这可以通过 require 来完成,也可以直接用代码写出模块来完成。

模块访问和实例化有点难以理解,最好这样描述:

  1. 模块访问正在运行阶段级别 1(宏/编译时代码),而不是运行阶段级别 0(运行时代码)。

  2. 模块实例化正在运行阶段级别 0 代码,但不是阶段级别 1 代码。

现在,棘手的一点是,要运行阶段级别 0 代码,您必须事先运行所有更高级别的阶段代码(如果尚未编译)。但是,如果该模块已被访问(并编译),它将不会再次运行阶段 1 代码。

这可以通过以下名为 test.rkt 的模块看出:

#lang racket

(require (for-meta 2 racket/base))
(displayln "phase 0")
(begin-for-syntax
  (displayln "phase 1")
  (begin-for-syntax
    (displayln "phase 2")))

该模块具有在阶段 0、阶段 1 和阶段 2(宏扩展阶段的宏扩展)运行的代码。在每个阶段中,它都会打印出一行来指示该阶段正在运行。使用这个模块,我们可以看到模块何时被实例化。

现在,让我们创建以下文件来实例化 test.rkt:

#lang racket
(dynamic-require "test.rkt" #f)

如果我们在 DrRacket 中运行它,输出将类似于:

phase 2
phase 1
phase 0

现在,我们看到阶段 0,因为模块正在实例化。然而,在这种情况下,我们还看到了阶段 2 和阶段 1,因为模块的语法阶段必须实例化才能运行阶段 0 代码。

但是,如果您再次运行它(假设您打开了已编译文件的缓存),您将得到:

phase 0

在这种情况下,您只能看到阶段 0,因为阶段 1 及更高版本的代码已被扩展。我们还可以将 0 赋予动态 require 以获得类似的结果。

现在,如果我们不是将 0#f 传递给 dynamic-require,而是传递 (void),给我们以下文件:

#lang racket
(dynamic-require "test.rkt" (void))

然后输出将是(再次假设您打开了编译缓存),将如下所示:

phase 1

这是因为这里运行的是宏观级别(阶段 1)代码,而不是运行时(阶段 0)代码。尽管如果我们对 test.rkt 进行轻微更改并再次保存(以使缓存失效),我们将得到:

phase 2
phase 1

这是因为 Racket 必须扩展第 2 阶段 代码才能运行第 1 阶段 代码。现在编译的模块缓存已更新,如果再次运行,将仅输出:

phase 1

最后,没有任何方法可以直接使用 dynamic-require 来保证代码高于阶段级别 1(据我所知,如果我错了,请更新此内容)将运行。

关于syntax - 访问、实例化和声明模块之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37336657/

相关文章:

Java gl 着色器语法错误

JavaScript 语法 : indexing object literals syntactically forbidden?

python - 绕过 pip 卸载的确认提示

linux - 嵌入式linux驱动加载

c++ - 无法验证我的父类(super class)或基类是否是宏或模板所声称的那样

bash - 我们什么时候需要用大括号括住 shell 变量?

javascript - 当伪 javascript 类已经初始化时,我在尝试修改 "this"对象时遇到了很多麻烦

macros - 在宏中保存参数的符号名称

clojure - 在 clojure 中使用非命名空间符号

vba - .Value = ""和 .ClearContents 之间有什么区别?