Racket 中的函数如module->language-info
, module->imports
,和module->exports
期望它们的模块被声明,但不一定被访问或实例化。
现在,dynamic-require
似乎有几个关于如何请求模块的选项,包括访问和实例化。
这让我想知道,声明模块、访问模块和实例化模块有什么区别?
最佳答案
声明一个模块只是该模块位于当前命名空间中的某个位置。这可以通过 require 来完成,也可以直接用代码写出模块来完成。
模块访问和实例化有点难以理解,最好这样描述:
模块访问正在运行阶段级别 1(宏/编译时代码),而不是运行阶段级别 0(运行时代码)。
模块实例化正在运行阶段级别 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/