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

4

Racket中的函数(例如module->language-infomodule->importsmodule->exports)需要声明其所在的模块,但不一定需要访问或实例化该模块。

现在,dynamic-require似乎有几个选项可以用于要求模块,包括访问和实例化。

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

1个回答

4
声明一个模块就是在当前命名空间中声明该模块的位置。这可以通过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

在这种情况下,您只能看到phase 0,因为phase 1和更高版本的代码已经被展开。我们也可以将0传递给动态要求以获得类似的结果。
现在,如果我们不是将0#f传递给dynamic-require,而是传递(void),则会得到以下文件:
#lang racket
(dynamic-require "test.rkt" (void))

假设您已经开启编译缓存,那么输出结果将会是这样的:
phase 1

这是因为此处正在运行宏层面(阶段1)的代码,而不是运行时(阶段0)的代码。尽管如果我们对test.rkt做出微小更改并再次保存(以使缓存失效),我们将会得到:
phase 2
phase 1

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

最后,据我所知(如果我错了,请更新),没有直接使用dynamic-require来确保高于1级的阶段代码将被运行的方法。


如果我可以给这个点赞两次的话,我会这么做。这是我能找到的唯一一个合理的解释,为什么你要将(void)传递给dynamic-require函数。 - Nate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接