是的,你需要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))
(我还把它扩展成 alist
以查看所有形式都被转换了。)
但是,如果您想在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
并使其递归地扫描其输入,但这是您不想进入的滑坡。(您需要为 a 内quote
、绑定内等地方制作特殊情况。)
在 Racket(以及最近在 Guile 中)解决这个问题的方法是使用语法参数。将此视为使用 绑定f
到相同的无用宏define-syntax-parameter
,然后使用syntax-parameterize
将其在 a 中的含义“调整”为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)))