Scheme宏扩展:在define-syntax中嵌入let-syntax

时间:2012-01-24 13:46:56

标签: macros scheme define-syntax

我希望扩展

(foo x (f n) (f n) (arbitrary) (f n) ...)

(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是未定义标识符的错误。我认为这是范围界定中的一个问题,这种宏扩展是否可能?

1 个答案:

答案 0 :(得分:3)

是的,您需要从某处获取f - 您的宏只是弥补它,因此foo的用户看不到它。当你考虑到你需要从某个地方获得它时,问题是你从哪里得到它?这是代码的固定版本,它假定它是foo的第二个子表单中的第一个:

(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))

(我还将其扩展为list以查看所有表单都已转换。)

但是,如果您希望在f中使用全局foo,那么您必须这样做:定义全局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。将此视为使用define-syntax-parameterf绑定到相同的无用宏,然后使用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)))