module->language-info
,module->imports
和module->exports
等球拍中的功能需要声明其模块,但不一定要访问或实例化。
现在,dynamic-require
似乎有几个关于如何要求模块的选项,包括访问和实例化。
这让我想知道,声明模块,访问模块和实例化模块有什么区别?
答案 0 :(得分:3)
声明一个模块只是该模块位于当前命名空间中的某个位置。这可以通过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
现在,我们看到phase 0
,因为模块正在实例化。但是,在这种情况下,我们还会看到phase 2
和phase 1
,因为模块的语法阶段必须实例化才能运行phase 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 2
代码才能运行phase 1
代码。既然已经更新了已编译的模块缓存,如果再次运行它,它将仅输出:
phase 1
最后,没有(据我所知,如果我错了就请更新),可以直接使用dynamic-require
来保证高于阶段1的代码运行