如何控制Scheme宏展开的顺序?

How to control order of Scheme macro expansion?

我正在使用 Racket 宏扩展 syntax-id-rules,其他一些 Scheme 实现以名称 identifier-syntax 提供。这些允许您指定即使定义的标识符不在头部位置也会发生的宏扩展。例如:

(define hidden #f)
(define-syntax proxy
  (syntax-id-rules (set!)
    [(set! proxy v) (set! hidden v)]
    [proxy hidden]))

将设置标识符 proxy 作为 hidden 的代理。这是一个无用的例子,但它说明了用法。

我发现自己处于这样一种情况,我想要一个全局普通宏,我们称之为 foo,在某些情况下我想覆盖它,我正在使用像 proxy 这样的标识符宏.也就是说,我希望能够做这样的事情:

(define-syntax foo
  (syntax-rules ()
    [(foo arg ...) 'default]))

(define hidden #f)
(define-syntax proxy
  (syntax-id-rules (foo set!)
    [(foo proxy arg ...) 'special]
    [(set! proxy v) (set! hidden v)]
    [proxy hidden]))

(foo proxy) ; should return 'special

但实际上最后一行 returns 'default,因为 foo 宏在 proxy 宏之前展开。

有什么想法我可以按照这些思路实现某些东西,但使用 proxy 标识符宏覆盖 foo 的默认宏定义?我不专门致力于上述架构。

补充:这不是用于任何现实世界的用法,而是形式语义学理论点演示的一部分。

在我看来,您需要找到替代策略。如果您提供更多关于您想要使用它的情况的详细信息,也许我们可以找到解决方案。

无论如何,这就是您的策略不起作用的原因。当你写

(define-syntax proxy ...)

您将语法转换器与标识符 proxy 相关联。当扩展器看到 (proxy ...)(set! proxy ...)proxy 时,它会调用该转换器。

为了控制 (foo proxy arg ...) 扩展到的内容,您需要在与 foo 关联的语法转换器中指定它。

现在看情况还是有招数可以玩的

例如,可以想象用一种新形式包装您的程序,将 (foo proxy arg ...) 重写为 (proxy 'was-a-foo-originally arg ...),然后让 proxy 的语法转换器处理其余部分。

简单的解决方案是将 (foo proxy arg ...) 的处理移至 foo 的转换器中,但您特别要求 foo 未更改的解决方案。

@soegaard 解释得很完美。不修改宏扩展器就不能直接为所欲为。

为了扩展@soegaard 的回答,这里有一种方法可以模拟您的要求。它本质上做了一个 "double-dispatch" 宏扩展。正如 soegaard 指出的那样,根据您的目标,可能有更惯用的方法来实现您想要的。

#lang racket
(require (for-syntax syntax/parse))

(begin-for-syntax
  (define (special-condition? id)
    (and (identifier? id)
         (regexp-match #rx"^p" ; starts with "p"
                       (symbol->string (syntax->datum id))))))

(define-syntax foo
  (syntax-parser
    [(_ special-case arg ...)
     #:when (special-condition? #'special-case)
     #'(special-case 'hidden-special-case-tag arg ...)]
    ; else
    [(_ arg ...) #''default]))

(define hidden #f)
(define-syntax proxy
  (syntax-id-rules (quote set!)
    [(proxy (quote hidden-special-case-tag) arg ...) 'special]
    [(set! proxy v) (set! hidden v)]
    [(proxy arg ...) 'other]
    [proxy hidden]))

(foo non-proxy) ; => 'default
(foo proxy) ; => 'special
(proxy) ; => 'other
proxy ; => #f
(set! proxy #t)
proxy ; => #t