编译时 Scheme 在宏中找不到函数

Scheme can't find function inside macro while compile

我有这样的示例代码:

#!/usr/bin/guile -s
!#

(define (process body)
    (list 'list (map (lambda (lst)
                       (list 'quote (car lst)))
                       body)))

(defmacro macro (body)
  (list 'quote (process body)))

(display (macro ((foo bar) (bar baz))))
(newline)

它 运行 但我从编译器那里得到错误

ERROR: Unbound variable: process

宏中的函数应该是允许的,为什么我会得到这个错误?

中的函数 允许在 Guile 和大多数其他 Scheme 方言中使用。

但是,关键的问题是:宏在扩展过程中可以调用哪些函数?


可以这样想:当编译器处理您的代码时,它首先专注于将您的源代码变成将来某个时候可以 运行 的东西。但是编译器在编译它们时可能不一定能够立即执行这些相同的函数,同时您的宏正在 运行 宁和扩展源代码。

  • 为什么没有这样的功能?好吧,一个例子是:如果函数体 使用了 您正在定义的宏怎么办?那么你会遇到一点 chicken/egg 问题。该函数需要 运行 编译宏(因为主体中的宏使用需要在编译时扩展)......但是宏需要可用的编译函数才能 运行!

(此外,可能有一些函数您 希望在编译时可用,作为您的宏的助手,但您不想成为在 运行 时间可用,因此在部署时它不会包含在您的程序可执行文件中,因为这会浪费 space 在已部署的二进制文件中。)

我最喜欢的一篇描述这个问题的论文,以及 MzScheme(现在称为 Racket)采用的特定解决方案,是 Matthew Flatt 的 "You Want It When" 论文。


因此,这是任何具有过程宏系统的Scheme方言都必须以某种方式处理的问题,Guile也不例外。

对于 Guile 的情况,Guile 手册中直接记录的一种修复方法是使用 eval-when 特殊形式,它允许您指定特定定义在哪些阶段可用。

(上面引用的 "You Want It When" 论文描述了 eval-when 的一些问题,但由于它是 Guile 手册文档的内容,我现在将坚持使用它。我确实建议在你理解 eval-when 之后,你会研究 Racket 的解决方案,看看 Guile 是否提供类似的东西。)


所以在你的情况下,因为你希望 process 函数在编译时可用(用于宏定义),你可以写:

#!/usr/bin/guile -s
!#

(eval-when (expand)
  (define (process body)
    (list 'list (map (lambda (lst)
                       (list 'quote (car lst)))
                     body))))

(defmacro macro (body)
  (list 'quote (process body)))

(display (macro ((foo bar) (bar baz))))
(newline)