Racket 在宏中调用其他函数

Racket 在宏中调用其他函数,racket,Racket,如何调用宏中的其他函数/宏?以下内容似乎不起作用(即使我使用define syntax定义bar) Racket的宏系统在运行时代码和编译时代码之间保持谨慎的分离。您将bar定义为一个运行时函数,但实际上您希望在宏中使用它。因此,您需要在编译时用beginforsyntax包装bar,以明确定义它: (begin-for-syntax (define (bar) #'"hello")) 这会解决你的问题。有关更多信息,请参阅《球拍指南》中的 为什么这是必要的?嗯,各种原因。首先,通过显式

如何调用宏中的其他函数/宏?以下内容似乎不起作用(即使我使用
define syntax
定义
bar


Racket的宏系统在运行时代码和编译时代码之间保持谨慎的分离。您将
bar
定义为一个运行时函数,但实际上您希望在宏中使用它。因此,您需要在编译时用
beginforsyntax
包装
bar
,以明确定义它:

(begin-for-syntax
  (define (bar) #'"hello"))
这会解决你的问题。有关更多信息,请参阅《球拍指南》中的


为什么这是必要的?嗯,各种原因。首先,通过显式区分运行时代码和编译时代码,编译器可以确定何时加载代码。例如,您可以在宏的实现中使用库,但可能永远不会在运行时使用库。通过注意将运行时和编译时分开,编译器可以确保只在编译时加载库,而不是在运行时加载库

在Racket中,我们调用代码可以运行阶段的不同时间,并为每个阶段分配一个编号。例如,运行时是阶段0,编译时是阶段1。为什么要用数字呢?事实证明,这不仅仅是两个阶段!事实上,Racket中可以有任意数量的编译阶段,继续第2阶段、第3阶段等等

所以,如果阶段1是编译时,那么阶段2是什么?那么,如果在另一个宏的实现中使用一个宏呢?如果您直接尝试,它将不起作用:

(define-syntax (foo stx)
  (syntax-case stx ()
    [(_) #''foo]))

(define-syntax (bar stx)
  (syntax-case stx ()
    [(_) (foo)]))
上述程序将再次抱怨
foo
未绑定,因为
foo
是在阶段0定义的,但
bar
内的代码是在阶段1。因此,我们需要将
foo
的定义包装在
beginforsyntax
中,就像前面一样:

(begin-for-syntax
  (define-syntax (foo stx)
    (syntax-case stx ()
      [(_) #''foo])))
但问题是:在哪个阶段实现
foo
at的代码?它显然不是阶段0,因为它是一个宏,但也不是阶段1,因为它是在编译时定义的宏(因为它包装在
beginforsyntax
)。因此,
foo
的主体处于第2阶段

事实上,如果您尝试编写上述代码,您可能会得到一些错误,很多东西都是未绑定的。编写
#lang racket
时,会在第0阶段和第1阶段自动导入内容,但一般来说,模块也只会在各个阶段导入。要使上述代码段正常工作,我们需要在第2阶段导入
racket/base
,如下所示:

(require (for-meta 2 racket/base))

阶段的所有细节都超出了这个答案的范围,但我要说的是,Racket中的阶段很重要,当你编写宏时,你必须担心它们。要获得更彻底的治疗,请参阅《球拍指南》中的,它补充并扩展了先前链接的介绍部分。有关相位电平为何重要以及在没有相位分离的情况下会出现什么问题的更多详细信息,请参阅本文(非常可读)的第一部分,该部分首先介绍了Racket的模块系统。

回答得很好-谢谢你的解释!
(require (for-meta 2 racket/base))