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

时间:2015-08-14 13:26:45

标签: macros scheme racket define-syntax syntax-rules

我正在使用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 { {1}}。也就是说,我希望能够做到这样的事情:

(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

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

我是如何在这些方面实现某些目标的,但是使用proxy标识符宏覆盖foo的默认宏定义?我并没有特别致力于上述架构。

补充:这不适用于任何实际使用,但是在形式语义学中证明理论点的一部分。

2 个答案:

答案 0 :(得分:3)

在我看来,你需要找到另一种策略。如果您提供有关您想要使用它的情况的更多详细信息,我们可以找到解决方案。

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

(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未更改的解决方案。

答案 1 :(得分:3)

@soegaard完美地解释了这一点。如果不修改宏扩展器,则无法直接执行所需操作。

为了扩展@ soegaard的答案,这里有一种模拟你要求的方法。它本质上是一个“双重调度”的宏观扩张。正如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