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

时间:2016-05-20 01:42:08

标签: syntax module macros racket

module->language-infomodule->importsmodule->exports等球拍中的功能需要声明其模块,但不一定要访问或实例化。

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

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

1 个答案:

答案 0 :(得分:3)

声明一个模块只是该模块位于当前命名空间中的某个位置。这可以通过require来完成,或者只是在代码中按字面意思写出模块。

模块访问和实例化理解起来有点棘手,最好这样描述:

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

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

  3. 现在,棘手的一点是,要运行阶段级别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 2phase 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的代码运行