Compiler construction 是否可以实现公共Lisp';计划中的宏观系统?

Compiler construction 是否可以实现公共Lisp';计划中的宏观系统?,compiler-construction,macros,scheme,common-lisp,Compiler Construction,Macros,Scheme,Common Lisp,希望这不是一个多余的问题 作为scheme的新手,我知道语法case宏比语法规则替代方案更强大,但代价是不必要的复杂性 然而,是否有可能在scheme中实现Common Lisp的宏系统,它比语法规则更强大,使用语法大小写 下面是Guile对定义宏的实现。注意,它完全是用语法case实现的: (define-syntax define-macro (lambda (x) "Define a defmacro." (syntax-case x () ((_ (mac

希望这不是一个多余的问题

作为scheme的新手,我知道
语法case
宏比
语法规则
替代方案更强大,但代价是不必要的复杂性


然而,是否有可能在scheme中实现Common Lisp的宏系统,它比
语法规则
更强大,使用
语法大小写

下面是Guile对
定义宏的实现。注意,它完全是用
语法case
实现的:

(define-syntax define-macro
  (lambda (x)
    "Define a defmacro."
    (syntax-case x ()
      ((_ (macro . args) doc body1 body ...)
       (string? (syntax->datum #'doc))
       #'(define-macro macro doc (lambda args body1 body ...)))
      ((_ (macro . args) body ...)
       #'(define-macro macro #f (lambda args body ...)))
      ((_ macro transformer)
       #'(define-macro macro #f transformer))
      ((_ macro doc transformer)
       (or (string? (syntax->datum #'doc))
           (not (syntax->datum #'doc)))
       #'(define-syntax macro
           (lambda (y)
             doc
             #((macro-type . defmacro)
               (defmacro-args args))
             (syntax-case y ()
               ((_ . args)
                (let ((v (syntax->datum #'args)))
                  (datum->syntax y (apply transformer v)))))))))))
Guile特别支持常见的Lisp样式的docstring,因此如果您的Scheme实现不使用docstring,那么您的
define macro
实现可能会更简单:

(define-syntax define-macro
  (lambda (x)
    (syntax-case x ()
      ((_ (macro . args) body ...)
       #'(define-macro macro (lambda args body ...)))
      ((_ macro transformer)
       #'(define-syntax macro
           (lambda (y)
             (syntax-case y ()
               ((_ . args)
                (let ((v (syntax->datum #'args)))
                  (datum->syntax y (apply transformer v)))))))))))

以下是my中的
define macro
的实现,以及Paul Graham书中的示例:

(define-syntax (define-macro x)
  (syntax-case x ()
    ((_ (name . args) . body)
      (syntax (define-macro name (lambda args . body))))
    ((_ name transformer)
      (syntax
       (define-syntax (name y)
         (syntax-case y ()
           ((_ . args)
             (datum->syntax-object
               (syntax _)
               (apply transformer
                 (syntax-object->datum (syntax args)))))))))))

(define-macro (when test . body) `(cond (,test . ,body)))

(define-macro (aif test-form then-else-forms)
  `(let ((it ,test-form))
     (if it ,then-else-forms)))

(define-macro (awhen pred? . body)
  `(aif ,pred? (begin ,@body)))
我会尽量简短——这很难,因为这通常是一个非常深刻的问题,超过了问答的平均水平。。。所以这仍然需要很长时间。我也会尽量不带偏见;尽管我是从球拍的角度来看的,但我过去一直在使用CommonLisp,而且我总是喜欢在这两个世界中使用宏(实际上在其他世界中也是如此)。它看起来不像是对你的问题的直接回答(在极端情况下,它只是“是”),但它是在比较两个系统,希望这将有助于澄清问题,尤其是对于那些不懂lisp(所有语言)的人来说,他们会问为什么这是一个大问题。我还将描述如何在“syntax case”系统中实现
defmacro
,但这只是为了让事情更清楚(因为您可以找到这样的实现,一些实现在注释和其他答案中给出)

首先,您的问题并不是多余的——它是非常合理的,而且(正如我所暗示的)是Lisp新手到Scheme和Scheme新手到Lisp遇到的事情之一

第二,非常肤浅、非常简短的回答是人们告诉您的:是的,可以在支持
语法case
的方案中实现CL的
defmacro
,并且正如预期的那样,您有多个指向此类实现的指针。相反,使用simple
defmacro
实现
syntax-case
是一个更棘手的主题,我不会谈论太多;我只想说这一点,只是重新实现
lambda
和其他绑定构造的成本非常高,这意味着它基本上是一种新语言的重新实现,如果您想使用该实现,您应该承诺使用该语言

还有一点需要澄清:人们,尤其是办事员,经常会破坏与Scheme宏相关的两件事:卫生和语法规则。问题是,在R5R中,您只有
语法规则
,这是一个非常有限的基于模式的重写系统。与其他重写系统一样,您可以天真地使用它,也可以一直使用重写来定义一种小型语言,然后使用它编写宏。有关如何完成此操作的已知解释,请参阅。虽然有可能做到这一点,但底线是这很难,你正在使用一些与你的实际语言没有直接关系的奇怪的小语言,这使得它与Scheme编程相去甚远——可能更糟糕的是,在CL中使用卫生宏实现并不是真正使用普通CL。简言之,可以只使用
语法规则
,但这主要是理论意义上的,而不是您希望在“真实”代码中使用的东西。这里的要点是,卫生并不意味着仅限于
语法规则

然而,
syntax rules
并不是作为“Scheme”宏系统设计的——它的想法是,您总是有一些“低级”宏实现,用于实现
syntax rules
,但也可以实现破坏卫生的宏——只是在特定的低级实现上没有达成一致意见。R6RS通过标准化“syntax-case”宏系统(注意,我使用“syntax-case”作为系统名称,不同于
syntax-case
,后者是它的主要亮点)。似乎是为了表明讨论仍然存在,R7RS退了一步并将其排除在外,恢复到使用
语法规则
,在低级系统上没有承诺,至少就“小型语言”而言是这样

现在,要真正理解这两个系统之间的区别,最好澄清的是它们所处理的类型之间的区别。使用
defmacro
,转换器基本上是一个函数,它接受S表达式并返回S表达式。这里的S表达式是由一系列文字类型(数字、字符串、布尔)、符号和这些类型的列表嵌套结构组成的类型。(实际使用的类型稍多于此,但这足以说明问题。)问题是,这是一个非常简单的世界:你得到的东西非常具体——你可以打印输入/输出值,这就是你所拥有的一切。请注意,这个系统使用符号来表示标识符——符号在这个意义上是非常具体的:一个
x
就是一段只有这个名称的代码,
x

然而,这种简单性是有代价的:您不能将其用于宏,因为您无法区分两个不同的标识符,这两个标识符都称为
x
。通常基于CL的
defmacro
确实有一些额外的位来补偿其中的一些。一个这样的位是
gensym
——一个用于创建不感兴趣的“新”符号的工具,因此保证与任何其他符号(包括具有相同名称的符号)不同。另一个这样的位是
&环境