Syntax Rules Examples

例子

这些例子显示了 syntax-rules 里面最容易搞混淆的地方。

(define mydef "mydef defined in toplevel")
(define aa 5)

(define-syntax test-macro
  (syntax-rules (mydef)
    ((_) #f)
    ((_ mydef bar st1 st2 ...) 
     (letrec ((bar 
               (lambda ()
                 (display "this is: ")
                 (display bar)
                 (newline)
                 (display "mydef is bound to: ")
                 (display mydef)
                 (newline))))
       st1 st2 ...))
    ((_ e1 e2 e3 ...)
     (let ((t e1))
       (display "aa's value is: ")
       (display aa)
       (newline)
       (if t t (or e2 e3 ...))))))

(test-macro mydef foo (foo))
(test-macro mydef bar (bar))

结果是:

> this is: #<procedure:foo>
mydef is bound to: mydef defined in toplevel
> this is: #<procedure:bar>
mydef is bound to: mydef defined in toplevel

这里显示了:

  1. literal mydef 匹配成功。因为它们的绑定都是顶层的 define 定义的。
  2. 如果要能从输入调用宏内部定义的函数,必须把它作为一个参数 传递进去。就像这里的 foo 和 bar。它们被传递进去之后得到了 定义,然后我们可以从输入调用它们。

接下来:

(let ((mydef "mydef defined in let"))
  (test-macro mydef bar (bar)))

结果是:

> aa's value is: aa defined in toplevel
"mydef defined in let"

这个例子显示了:

  1. 对于 literal mydef: syntax-rules 出现的地方,mydef 的绑定是顶层的 define 定义 的。而在这里我们在 let 中重新绑定了 mydef 的值。所以第一 条规则不匹配。
  2. aa 的值是最顶层的定义。

接下来:

(define mydef #f)
(test-macro mydef bar (bar))

结果是:

> this is: #<procedure:bar>
mydef is bound to: #f

这说明,literal mydef 仍然匹配,虽然它们的顶层定义改变了。

接下来我们把 test-macro 定义在一个 let 块里:

(let ((mydef "mydef defined in syntax block")
      (aa "aa in outer syntax block"))
  (define-syntax test-macro
    (syntax-rules (mydef)
      ((_) #f)
      ((_ mydef bar st1 st2 ...) 
       (letrec ((bar 
                 (lambda ()
                   (display "this is: ")
                   (display bar)
                   (newline)
                   (display "mydef is bound to: ")
                   (display mydef)
                   (newline))))
         st1 st2 ...))
      ((_ e1 e2 e3 ...)
       (let ((t e1))
         (display "aa's value is: ")
         (display aa)
         (newline)
         (if t t (or e2 e3 ...))))))

  (test-macro mydef bar (bar))

  (let ((mydef "mydef defined in inner let")
        (aa "aa defined in inner let block"))
    (test-macro mydef bar (bar)))

  )

  (set! aa "aa definition changed in outer let")
  (let ((mydef "mydef defined in inner let")
        (aa "aa defined in inner let block"))
    (test-macro mydef bar (bar)))

  )

结果是:

> this is: #<procedure:bar>
mydef is bound to: mydef defined in syntax block
aa's value is: aa in outer syntax block
aa's value is: aa definition changed in outer let
"mydef defined in inner let"

这说明:

  1. 第一个 literal mydef 匹配,因为它们都是在外层 let 块里绑定的。
  2. 第二个 literal mydef 不匹配,因为它是在内层 let 块里定 义的。
  3. aa 的值是在外层 let 块里的值。即使它在内层定义过,但是 syntax-rules 是出现在外层,所以它使用外层的值,这个值可以 改变。

接下来:

(let ((aa 6)
      (mydef "mydef in let block"))
  (test-macro mydef bar (bar)))

结果是:

> aa's value is: aa defined in toplevel
"mydef in let block"

虽然在 let 内重新绑定了 aa 的值,但是由于这时我们使用的是最 外层定义的语法 test-macro, 所以它的 aa 引用的是最外层的 aa.

重新定义 test-macro, 不把 mydef 作为 literal.

(define-syntax test-macro
  (syntax-rules ()
    ((_) #f)
    ((_ mydef bar st1 st2 ...) 
     (letrec ((bar 
               (lambda ()
                 (display "this is: ")
                 (display bar)
                 (newline)
                 (display "mydef is bound to: ")
                 (display mydef)
                 (newline))))
       st1 st2 ...))
    ((_ e1 e2 e3 ...)
     (let ((t e1))
       (display "aa's value is: ")
       (display aa)
       (newline)
       (if t t (or e2 e3 ...))))))

(let ((mydef "mydef defined in let"))
  (test-macro mydef bar (bar)))

结果是:

> this is: #<procedure:bar>
mydef is bound to: mydef defined in let

这个例子里的 mydef 不是一个 literal,所以它即使在 let 内部重 新绑定,仍然可以匹配。而且它的值是重新绑定以后的值。

这就让我想起 R5RS 里的例子:

(let ((=> #f))
  (cond (#t => 'ok)))

结果是 ok. 这里 => 虽然在内部被重新绑定。但是 cond 是在顶层 定义的,所以只有顶层定义的 => 才能匹配这个 "=>".

所以

(cond (#t => 'ok))

无论你是否在顶层重新定义 => 都是要出错的。