【问题标题】:What is the difference between visiting, instantiating, and declaring a module?访问、实例化和声明模块有什么区别?
【发布时间】:2016-05-20 01:42:08
【问题描述】:

诸如module->language-infomodule->importsmodule->exports 等球拍中的函数期望它们的模块被声明,但不一定被访问或实例化。

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

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

【问题讨论】:

    标签: syntax module macros racket


    【解决方案1】:

    声明一个模块只是这个模块在当前命名空间的某个地方。这可以通过 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
    

    现在,我们看到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的代码都会运行的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-12
      • 2013-02-08
      • 2018-04-28
      • 2011-01-20
      • 2012-07-15
      • 2010-11-27
      相关资源
      最近更新 更多