【问题标题】:Scheme can't find function inside macro while compile编译时Scheme在宏内找不到函数
【发布时间】:2017-08-20 14:39:21
【问题描述】:

我有这样的示例代码:

#!/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

应该允许宏中的函数,为什么会出现这个错误?

【问题讨论】:

    标签: macros scheme guile


    【解决方案1】:

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

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


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

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

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

    我最喜欢描述这个问题的论文之一,以及 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 2016-07-30
      • 2019-03-17
      • 2011-11-13
      • 2021-10-12
      • 2014-04-05
      相关资源
      最近更新 更多