2012-01-24 37 views
1

我希望扩大方案宏展开:嵌套让语法内定义的语法

​​

(begin (x 'f n) (x 'f n) (arbitrary) (x 'f n) ...) 

我的尝试是:

(define-syntax foo 
    (syntax-rules() 
    ((_ l a ...) 
    (let-syntax ((f (syntax-rules() 
         ((_ n) (l (quote f) n))))) 
     (begin a ...))))) 

(define (x t1 t2) (cons t1 t2)) ;; for example only 
(define (arbitrary) (cons 'a 'b)) ;; for example only 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

使用宏步进我可以看到宏的第一阶段扩展为

(let-syntax ((f (syntax-rules() ((_ n) (x 'f n))))) 
    (begin (f 1) (f 2) (arbitrary) (f 3))) 

其中,当在隔离评估完美,但作为一个整体执行时,我得到一个关于f是一个未定义的标识符的错误。我认为这是一个范围问题,这种类型的宏观扩展是可能的吗?

回答

3

是的,你需要从得到f - 你的宏只是补充了它,因此它对foo的用户是不可见的。当你认为你需要从某个地方得到它时,问题是你从哪里得到它?下面是假定它是在foo第二子窗体的第一件事,你的代码的固定版本(我也把它做成了list扩大到看到所有形式的转化)

(define-syntax foo 
    (syntax-rules() 
    [(_ l (f a) more ...) 
    (let-syntax ([f (syntax-rules() 
         [(_ n) (l 'f n)])]) 
     (list (f a) more ...))])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

但是,如果您想在foo内使用全局类型的f,那么您确实必须这样做:定义全局f。这里做一个有限的方式:

;; no body => using `f' is always an error 
(define-syntax f (syntax-rules())) 

(define-syntax foo 
    (syntax-rules() 
    [(_ l a ...) (list (foo-helper l a) ...)])) 
(define-syntax foo-helper 
    (syntax-rules (f) ; match on f and transform it 
    [(_ l (f n)) (l 'f n)] 
    [(_ l a)  a])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

在这方面的主要限制是,它只会当的a形式之一是使用f工作 - 如果它嵌套在一个表达式,但它不会工作。例如,这将引发一个语法错误:

(foo x (f 1) (f 2) (arbitrary) 
     (let ([n 3]) (f n))) 

你能想象复杂foo-helper并使其循环扫描其输入,但是这就是你不想陷入滑坡。 (您需要为quote内的地方制作特殊情况,如绑定等)

在Racket(最近也在Guile中)解决此问题的方法是使用syntax parameter。可以考虑将f绑定到使用define-syntax-parameter的相同无用宏,然后使用syntax-parameterize将其在foo内的含义“调整”为执行所需转换的宏。下面是这个样子:

;; needed to get syntax parameters 
(require racket/stxparam) 

;; same useless definition, but as a syntax parameter 
(define-syntax-parameter f (syntax-rules())) 

(define-syntax foo 
    (syntax-rules() 
    [(_ l a ...) 
    ;; adjust it inside these forms 
    (syntax-parameterize ([f (syntax-rules() 
           [(_ n) (l 'f n)])]) 
     (list a ...))])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) 
     (let ([n 3]) (f n))) 
+0

我希望顶层宏调用的身体会被原封不动的使用和评估展开形式的范围,其中'F'确实存在一个范围。您提供的解决方案并不理想,因为我打算让'foo'的主体包含任意表达式和已知关键字表达式(如'f')的混合。你可能会告诉我,我正在试图为传统的调度程序创建一个更干净的界面,我可能不得不依赖调度程序。谢谢。 – kjfletch

+0

我现在看到这是一个宏观卫生问题,而不是范围问题。 “f”不是一个已知的绑定,因此创建一个新的卫生绑定供内部使用。 – kjfletch

+0

宏观卫生*是一个范围问题:如果你想要一个全球知名的'f',那么你需要......做到这一点。我已经为此添加了两个示例。 –