问题
I'm working with the Racket macro extension syntax-id-rules
, that some other Scheme implementations provide under the name identifier-syntax
. These let you specify macro expansions that will happen even when the defined identifier isn't in head position. So for example:
(define hidden #f)
(define-syntax proxy
(syntax-id-rules (set!)
[(set! proxy v) (set! hidden v)]
[proxy hidden]))
will set up the identifier proxy
to be a proxy for hidden
. This is a useless example, but it illustrates the usage.
I find myself in a situation where I want a global ordinary macro, let's call it foo
, that I want to override in some cases where I'm using an identifier macro like proxy
. That is, I want to be able to do something like this:
(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
But in fact the last line returns 'default
, because the foo
macro gets expanded before the proxy
one.
Any ideas how I might achieve something along these lines, but with the proxy
identifier macro overriding the default macro definition for foo
? I'm not committed to the above architecture specifically.
Added: This isn't for any real-world usage, but part of a demonstration of a theoretical point in formal semantics.
回答1:
@soegaard explained it perfectly. You can't do what you want directly without modifying the macro expander.
To extend @soegaard's answer, here is a way to simulate what you are asking for. It essentially does a "double-dispatch" macro expansion. As soegaard noted though, there's probably a more idiomatic way to achieve what you want, depending on your goals.
#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
回答2:
It seems to me that you need to find an alternative strategy. Maybe we can find a solution if you provide more details of the situation you want to use this in.
Anyways, here is why your strategy don't work. When you write
(define-syntax proxy ...)
you associate a syntax-transformer with the identifier proxy
. That transformer is called by the expander when it sees either (proxy ...)
, (set! proxy ...)
, or proxy
.
In order to control what (foo proxy arg ...)
expands to you need to specify it in the syntax-transformer associated with foo
.
Now depending on the situation there might be tricks that can be played.
For example one could imagine wrapping your program in a new form, that rewrites (foo proxy arg ...)
into (proxy 'was-a-foo-originally arg ...)
and then let the syntax-transformer for proxy
handle the rest.
The easy solution is to move the handling of (foo proxy arg ...)
into the transformer for foo
, but you specifically ask for a solution where foo
isn't changed.
来源:https://stackoverflow.com/questions/32011251/how-to-control-order-of-scheme-macro-expansion