Scheme在编译时无法在宏内找到函数

时间:2017-03-27 18:37:33

标签: macros scheme guile

我有这样的示例代码:

#!/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
应该允许

宏中的函数,为什么我得到这个错误?

1 个答案:

答案 0 :(得分:3)

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

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

以这种方式思考:当编译器处理您的代码时,它首先专注于将您的源代码转换为可以在将来的某个时刻运行的代码。但是编译器可能不一定能够在编译它们的同时执行那些相同的功能,同时宏运行并扩展源代码。

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

(此外,可能有一些函数只是 希望在编译时可用,作为宏的帮助程序,但是您不希望在运行时可用,以便在部署时不会将其包含在程序可执行文件中,因为这会浪费部署的二进制文件中的空间。)

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

所以,这是一个问题,任何带有程序宏系统的Scheme方言都必须以某种方式处理,而Guile也不例外。

在Guile的案例中,Guile手册中直接记录的一个修正是使用eval-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)