2012-01-24 2 views
1

Я хотел бы расширитьМакрос схемы расширения: Верстка LET-синтаксис внутри определения-синтаксиса

(foo x (f n) (f n) (arbitrary) (f n) ...) 

в

(begin (x 'f n) (x 'f n) (arbitrary) (x 'f n) ...) 

моя попытка:

(define-syntax foo 
    (syntax-rules() 
    ((_ l a ...) 
    (let-syntax ((f (syntax-rules() 
         ((_ n) (l (quote f) n))))) 
     (begin a ...))))) 

(define (x t1 t2) (cons t1 t2)) ;; for example only 
(define (arbitrary) (cons 'a 'b)) ;; for example only 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

Использование макроса шагового I может видеть, что первая стадия макроса расширяется до

(let-syntax ((f (syntax-rules() ((_ n) (x 'f n))))) 
    (begin (f 1) (f 2) (arbitrary) (f 3))) 

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

ответ

3

Ага, вам необходимо f от где - макрос просто делает это, и поэтому она не видна пользователям foo. Когда вы считаете, что вам нужно достать его откуда-то, вопрос в том, откуда вы его получите? Вот Исправленная версия кода, который предполагает, что это первое, что во втором субформа foo: (. Я также сделал его расширения в list, чтобы увидеть, что все формы трансформируются)

(define-syntax foo 
    (syntax-rules() 
    [(_ l (f a) more ...) 
    (let-syntax ([f (syntax-rules() 
         [(_ n) (l 'f n)])]) 
     (list (f a) more ...))])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

Однако, если вы хотите, чтобы глобальный вид f использовался внутри foo, вам действительно нужно сделать именно это: определить глобальный f. Это ограниченный способ сделать это:

;; no body => using `f' is always an error 
(define-syntax f (syntax-rules())) 

(define-syntax foo 
    (syntax-rules() 
    [(_ l a ...) (list (foo-helper l a) ...)])) 
(define-syntax foo-helper 
    (syntax-rules (f) ; match on f and transform it 
    [(_ l (f n)) (l 'f n)] 
    [(_ l a)  a])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) (f 3)) 

Основное ограничение в том, что это будет работать только тогда, когда одна из форм a использует f - но это не будет работать, если он вложен в выражении , Например, это вызовет синтаксическую ошибку:

(foo x (f 1) (f 2) (arbitrary) 
     (let ([n 3]) (f n))) 

Вы можете себе представить, усложняя foo-helper и сделать его просканировать свой вклад рекурсивный, но это скользкий вы не хотите попасть. (Вам нужно будет сделать специальные случаи для таких мест, как внутри quote, в переплетах и ​​т. Д.)

Способ решения проблемы в Racket (и недавно в Guile тоже) заключается в использовании syntax parameter. Подумайте об этом как о привязке f к тому же бесполезному макросу, используя define-syntax-parameter, а затем используйте syntax-parameterize, чтобы «настроить» его значение внутри foo макросу, который выполняет преобразование, которое вы хотите. Вот как это выглядит:

;; needed to get syntax parameters 
(require racket/stxparam) 

;; same useless definition, but as a syntax parameter 
(define-syntax-parameter f (syntax-rules())) 

(define-syntax foo 
    (syntax-rules() 
    [(_ l a ...) 
    ;; adjust it inside these forms 
    (syntax-parameterize ([f (syntax-rules() 
           [(_ n) (l 'f n)])]) 
     (list a ...))])) 

(define (x t1 t2) (cons t1 t2)) 
(define (arbitrary) (cons 'a 'b)) 
(foo x (f 1) (f 2) (arbitrary) 
     (let ([n 3]) (f n))) 
+0

Я надеялся, что тело макровызова верхнего уровня будет принято как есть и оценено в рамках расширенной формы, область применения, в которых 'f' действительно существует , Решение, которое вы предлагаете, не является идеальным, поскольку я предполагаю, что тело 'foo' содержит смесь произвольных выражений и известных выражений ключевых слов (например,' f'). Вероятно, вы можете сказать, что я пытаюсь сделать более чистый интерфейс традиционному диспетчеру, мне, возможно, придется просто полагаться на диспетчера. Благодарю. – kjfletch

+0

Теперь я вижу, что это проблема макровизии, а не сферы. 'f' не является известным связыванием, поэтому для внутреннего использования создается новое гигиеническое связывание. – kjfletch

+0

Macro hygiene * is * проблема с областью: если вы хотите глобально известный 'f', тогда вам нужно ... сделать именно это. Я добавил два примера для этого. –