从 Racket 宏中取消引用
Unquoting from a Racket macro
我有一个函数可以对表达式进行一些处理:
(struct sym (s) #:transparent)
(define (foo-fn ex)
(match ex
[(? symbol? s) `(sym (quote ,s))]
[(? list? xs) (cons (car xs) (map foo-fn (cdr xs)))]
[x x]))
此函数按预期工作:
> (foo-fn '(cons x y))
'(cons (sym 'x) (sym 'y))
现在我正在尝试制作一个宏,它采用给定的表达式并将其替换为 foo-fn
的结果。我已经设法通过
获得了一些方法
(define-syntax-rule (foo ex)
(foo-fn (quote ex)))
但是,这个宏仍然给出了引用的表达式,我想要表达式本身。也就是说,虽然我当前的代码给出
> (foo (cons x y))
'(cons (sym 'x) (sym 'y))
我希望结果是
> (foo (cons x y))
(cons (sym 'x) (sym 'y))
我已经通过使用 eval
找到了解决方法,但我很确定这不是宏的使用方式(如果我错了请纠正我)
(define-syntax-rule (foo ex)
(eval (foo-fn (quote ex))))
虽然以上方法有效,但我认为这是使用宏的不正确方法。首选方法是什么?
这里的根本问题不在您的 foo
宏中,而在 foo-fn
中。 foo-fn
对数据进行操作,而不是语法对象,因此它会丢弃所有词汇上下文和源位置信息。这是不可逆的操作,因此无法从引用表达式“返回”到语法片段。
而不是使用 foo-fn
来执行源代码转换,看起来您可能希望 foo
宏自己进行语法分析。使用 syntax/parse/define
,这非常简单:
(require syntax/parse/define)
(struct sym (s) #:transparent)
(define-syntax-parser foo
[(_ s:id) #'(sym 's)]
[(_ (f x ...)) #'(f (foo x) ...)]
[(_ x) #'x])
通过将其实现为宏,您可以进行句法到句法的转换,而不是数据到数据的转换,这样可以适当地保留词法上下文。 (这种保存是被称为“宏观卫生”的概念的组成部分。)
现在,您可以使用 foo
宏并获得您期望的结果:
> (foo (cons x y))
(cons (sym 'x) (sym 'y))
我有一个函数可以对表达式进行一些处理:
(struct sym (s) #:transparent)
(define (foo-fn ex)
(match ex
[(? symbol? s) `(sym (quote ,s))]
[(? list? xs) (cons (car xs) (map foo-fn (cdr xs)))]
[x x]))
此函数按预期工作:
> (foo-fn '(cons x y))
'(cons (sym 'x) (sym 'y))
现在我正在尝试制作一个宏,它采用给定的表达式并将其替换为 foo-fn
的结果。我已经设法通过
(define-syntax-rule (foo ex)
(foo-fn (quote ex)))
但是,这个宏仍然给出了引用的表达式,我想要表达式本身。也就是说,虽然我当前的代码给出
> (foo (cons x y))
'(cons (sym 'x) (sym 'y))
我希望结果是
> (foo (cons x y))
(cons (sym 'x) (sym 'y))
我已经通过使用 eval
找到了解决方法,但我很确定这不是宏的使用方式(如果我错了请纠正我)
(define-syntax-rule (foo ex)
(eval (foo-fn (quote ex))))
虽然以上方法有效,但我认为这是使用宏的不正确方法。首选方法是什么?
这里的根本问题不在您的 foo
宏中,而在 foo-fn
中。 foo-fn
对数据进行操作,而不是语法对象,因此它会丢弃所有词汇上下文和源位置信息。这是不可逆的操作,因此无法从引用表达式“返回”到语法片段。
而不是使用 foo-fn
来执行源代码转换,看起来您可能希望 foo
宏自己进行语法分析。使用 syntax/parse/define
,这非常简单:
(require syntax/parse/define)
(struct sym (s) #:transparent)
(define-syntax-parser foo
[(_ s:id) #'(sym 's)]
[(_ (f x ...)) #'(f (foo x) ...)]
[(_ x) #'x])
通过将其实现为宏,您可以进行句法到句法的转换,而不是数据到数据的转换,这样可以适当地保留词法上下文。 (这种保存是被称为“宏观卫生”的概念的组成部分。)
现在,您可以使用 foo
宏并获得您期望的结果:
> (foo (cons x y))
(cons (sym 'x) (sym 'y))