【问题标题】:Unquoting from a Racket macro从 Racket 宏中取消引用
【发布时间】:2016-10-02 17:47:27
【问题描述】:

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

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

虽然上述方法有效,但我认为这是使用宏的错误方式。首选的方法是什么?

【问题讨论】:

    标签: macros racket quote


    【解决方案1】:

    这里的根本问题不在您的foo 宏中,而是在foo-fn 中。 foo-fn 对数据而不是语法对象进行操作,因此它丢弃了所有词汇上下文和源位置信息。这是一个不可逆的操作,因此无法从引用的表达式“返回”到语法片段。

    您可能希望foo 宏自己进行语法解析,而不是使用foo-fn 来执行源转换。使用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))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-24
      • 1970-01-01
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多