Scheme语言自定义语法结构时的Literal identifier
Scheme语言允许用户使用let-syntax等机制自定义特殊的语法结构。定义这种语法结构时使用的Pattern language里有一个不太显眼的Literal identifier,就是下面语法中的<literals>:
(syntax-rules <literals> <syntax rule> ... )
Scheme标准R5RS里对Literal identifier的阐释比较晦涩难懂。我根据自己的理解写了一个小例子,大概可以起到补充说明的作用:
;;; 不使用Literal identifier时定义语法z
(define-syntax z
(syntax-rules ()
((z (+ x y)) (+ x y))))
;;; 下面一行的结果是 7
(z (+ 3 4))
;;; 下面一行的结果是 -1,在局部改变意义后的加号起了作用
(let ((+ -)) (z (+ 3 4)))
;;; 使用Literal identifier时定义语法z
(define-syntax z
(syntax-rules (+)
((z (+ x y)) (+ x y))))
;;; 下面一行的结果仍是 7
(z (+ 3 4))
;;; 下面这一行报错:z: bad syntax in: (z (+ 3 4))
;;; 与定义语法时含义不同(其实是绑定不同)的加号无法应用到这样的语法中
(let ((+ -)) (z (+ 3 4)))
有了上面这个例子,R5RS里提到的那个较复杂的例子就比较好理解了:
(define-syntax cond
(syntax-rules (else =>)
((cond (else result1 result2 ...))
(begin result1 result2 ...))
((cond (test => result))
(let ((temp test))
(if temp (result temp))))
((cond (test => result) clause1 clause2 ...)
(let ((temp test))
(if temp
(result temp)
(cond clause1 clause2 ...))))
((cond (test)) test)
((cond (test) clause1 clause2 ...)
(let ((temp test))
(if temp
temp
(cond clause1 clause2 ...))))
((cond (test result1 result2 ...))
(if test (begin result1 result2 ...)))
((cond (test result1 result2 ...)
clause1 clause2 ...)
(if test
(begin result1 result2 ...)
(cond clause1 clause2 ...)))))
在上面这样的cond定义基础上,下面的代码
(let ((=> #f))
(cond (#t => 'ok))) ; =) ok
将被解释为(这里的=>被视为变量而不是语法元素)
(let ((=> #f))
(if #t (begin => 'ok)))
而不是
(let ((=> #f))
(let ((temp #t))
(if temp ('ok temp))))
因为局部的=>已经被改变了含义,不能匹配到包含=>的语法规则了。