具有语法规则的卫生和引用透明的宏

LISP 和 Scheme 相比其他主流编程语言的最大优势是它们的宏系统。与 C 预处理器和其他宏语言不同,Scheme 宏将解析后的代码作为输入,并将扩展代码作为输出返回。这是 Scheme 的代码是数据短语的应用之一,它使语言如此强大。

Scheme 中的宏是用 define-syntax 创建的,define-syntax 可以通过多种方式定义宏。最简单的方法是使用 syntax-rules,它使用模式匹配将输入代码转换为输出代码。

此示例创建一个简单的 for item in listfor list as item 语法,用于循环列表中的元素:

(define-syntax for
  (syntax-rules (in as) ; 'in' and 'as' keywords must match in the pattern
    ; When the 'for' macro is called, try matching this pattern
    ((for element in list
          body ...) ; Match one or more body expressions
     ; Transform the input code
     (for-each (lambda (element)
                 body ...)
               list))
    ; Try matching another pattern if the first fails
    ((for list as element
          body ...)
     ; Use the existing macro for the transform
     (for element in list
          body ...))))

然后可以按如下方式使用这两个宏,从而提供更强制的风格:

(let ((names '(Alice Bob Eve)))
  (for name in names
    (display "Hello ")
    (display name)
    (newline))
  (for names as name
    (display "name: ")
    (display name)
    (newline)))

运行代码将提供预期的输出:

Hello Alice
Hello Bob
Hello Eve
name: Alice
name: Bob
name: Eve

需要注意的最常见错误是没有将正确的值传递给宏,这通常会导致无效的错误消息应用于扩展形式而不是宏调用。

上面的 for 语法定义不检查它们是否传递了标识符和列表,因此传递任何其他类型将导致指向 for-each 调用而不是 for 调用的错误。调试这会破坏宏的目的,因此由用户将检查放在那里并报告使用错误,然后可以在编译时捕获。