Как контролировать порядок расширения макроса Схемы?

Я работаю с расширением Racket macro syntax-id-rules, что некоторые другие реализации Схемы предоставляют под именем 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

Но на самом деле последняя строка возвращает 'default, поскольку foo макрос раскрывается до proxy один.

Любые идеи, как я мог бы достичь чего-то в этом направлении, но с proxy макрос идентификатора, переопределяющий определение макроса по умолчанию для foo? Я не привержен вышеупомянутой архитектуре специально.

Добавлено: Это не для любого реального использования, но является частью демонстрации теоретической точки в формальной семантике.

2 ответа

Решение

@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

Мне кажется, что вам нужно найти альтернативную стратегию. Возможно, мы сможем найти решение, если вы предоставите более подробную информацию о ситуации, в которой вы хотите использовать это.

В любом случае, вот почему ваша стратегия не работает. Когда ты пишешь

(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 не изменился

Другие вопросы по тегам