2

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.

dubiousjim
  • 4,722
  • 1
  • 36
  • 34

2 Answers2

3

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.

soegaard
  • 30,661
  • 4
  • 57
  • 106
  • Added note in question about my "use case". I can't easily put the handling of `proxy` into `foo`, since there may be many `proxy`s, defined at different levels, but there should be only one catch-all default implementation of `foo`, where the arguments needed by `proxy`-like. – dubiousjim Aug 14 '15 at 18:10
3

@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
stchang
  • 2,555
  • 15
  • 17
  • I suspected I'd need to do two layers of macro-expansion; thanks for showing the skeleton code to make this happen. This may be good enough for my purposes, and it doesn't sound like I'm going to achieve better. Essentially what I'm needing is something like `syntax-id-rules` or `make-set!-transformer`, but where we can supply additional forms, like `foo`, that get the same special treatment that `set!` gets. – dubiousjim Aug 14 '15 at 18:12
  • `syntax-parser` doesn't seem to be needed here after all; one can achieve the same effect using the optional guard expressions on Racket's `syntax-case`: `(define-syntax (foo stx) (syntax-case stx () [(_ special-case arg ...) (special-condition? #'special-case) #'(special-case 'hidden-special-case-tag arg ...)] [(_ arg ...) #''default]))` – dubiousjim Aug 14 '15 at 20:48
  • Yes, of course you're right. I guess Racketeers generally prefer `syntax-parse` for its more expressive pattern language and better error-reporting facilities. – stchang Aug 17 '15 at 14:10