是的,你需要从得到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)))
我希望顶层宏调用的身体会被原封不动的使用和评估展开形式的范围,其中'F'确实存在一个范围。您提供的解决方案并不理想,因为我打算让'foo'的主体包含任意表达式和已知关键字表达式(如'f')的混合。你可能会告诉我,我正在试图为传统的调度程序创建一个更干净的界面,我可能不得不依赖调度程序。谢谢。 – kjfletch
我现在看到这是一个宏观卫生问题,而不是范围问题。 “f”不是一个已知的绑定,因此创建一个新的卫生绑定供内部使用。 – kjfletch
宏观卫生*是一个范围问题:如果你想要一个全球知名的'f',那么你需要......做到这一点。我已经为此添加了两个示例。 –