macros - 从 Racket 宏中取消引用

标签 macros racket quote

我有一个对表达式进行一些处理的函数:

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

关于macros - 从 Racket 宏中取消引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39819854/

相关文章:

algorithm - 函数式编程习惯用法,用于计算 racket/haskell 中不发生突变的最多 4 个数字

javascript - 如何在(javascript和ap.net)中将嵌套引号相互嵌套三遍

PHP 之间的区别 echo 'string' .'\n' ;并回显 'string' ."\n";

c++ - 用宏循环

macros - Lisp源代码重写系统

loops - Racket expt 是否使用尾递归?

lisp - 如何在 Racket 中计算非自然对数?

C++ 在 if 条件下使用 g++ 定义一个静态变量

c++ - 有没有跨平台的方法来检测 Debug模式编译?