如何在宏中调用其他函数/宏?以下内容似乎不起作用(即使我用define-syntax定义了define-syntax)
(define (bar) #'"hello")
(define-syntax (foo stx)
(syntax-case stx ()
[(_ '(a b)) (bar)]))发布于 2018-06-30 02:18:07
Racket的宏系统在运行时和编译时代码之间保持了仔细的分离。您将bar定义为运行时函数,但实际上希望在宏中使用它。因此,您需要在编译时显式定义bar,方法是用begin-for-syntax包装它。
(begin-for-syntax
(define (bar) #'"hello"))这会解决你的问题。有关更多信息,请参见球拍指南中的编译和运行时阶段。
为什么有这个必要?嗯,各种原因。首先,通过显式区分运行时代码和编译时代码,编译器可以对何时加载代码做出一定的保证。例如,您可能在宏的实现中使用一个库,但是您可能永远不会在运行时使用该库。通过注意分离运行时和编译时,编译器可以确保只在编译时加载库,而不是在运行时加载。
在Racket中,我们调用代码可以运行阶段的不同时间,并为每个阶段分配一个数字。例如,运行时是阶段0,编译时间是阶段1。为什么要使用数字呢?事实证明,这不仅仅是两个阶段!事实上,在Racket中可以有任意数量的编译阶段,继续到第2阶段、第3阶段等等。
那么,如果第一阶段是编译时,什么是第二阶段?那么,如果在实现另一个宏时使用宏呢?如果你直接尝试,它就不会起作用:
(define-syntax (foo stx)
(syntax-case stx ()
[(_) #''foo]))
(define-syntax (bar stx)
(syntax-case stx ()
[(_) (foo)]))再一次,上面的程序会抱怨foo没有绑定,因为foo是在0阶段定义的,但是bar内部的代码是在第1阶段。因此,我们需要像以前一样在begin-for-syntax中包装foo的定义:
(begin-for-syntax
(define-syntax (foo stx)
(syntax-case stx ()
[(_) #''foo])))但是问题是:实现foo的代码在哪个阶段?它显然不是第0阶段,因为它是一个宏,但是它也不是第1阶段,因为它是在编译时定义的(因为它是用begin-for-syntax包装的)。因此,foo的主体处于第二阶段!
实际上,如果您尝试编写上面的代码,您可能会得到一些错误,很多事情都是不绑定的。编写#lang racket时,在第0阶段和第1阶段自动导入东西,但通常,模块也只能在各个阶段导入。要使上面的代码片段正常工作,我们需要在第2阶段导入racket/base,如下所示:
(require (for-meta 2 racket/base))所有阶段的细节都超出了这个答案的范围,但我要指出的是,在Racket中的阶段是很重要的,当你在编写宏时,你必须担心它们。要获得更全面的处理,请参见“球拍指南”中的一般阶段水平,它补充并扩展了先前链接的介绍部分。有关相位级为什么重要以及在没有相位分离时出错的更多详细信息,请参阅论文可组合和可编译宏的(非常可读的)第一节,该部分首先介绍了Racket的模块系统。
https://stackoverflow.com/questions/51111128
复制相似问题