Ага, вам необходимо 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)))
Я надеялся, что тело макровызова верхнего уровня будет принято как есть и оценено в рамках расширенной формы, область применения, в которых 'f' действительно существует , Решение, которое вы предлагаете, не является идеальным, поскольку я предполагаю, что тело 'foo' содержит смесь произвольных выражений и известных выражений ключевых слов (например,' f'). Вероятно, вы можете сказать, что я пытаюсь сделать более чистый интерфейс традиционному диспетчеру, мне, возможно, придется просто полагаться на диспетчера. Благодарю. – kjfletch
Теперь я вижу, что это проблема макровизии, а не сферы. 'f' не является известным связыванием, поэтому для внутреннего использования создается новое гигиеническое связывание. – kjfletch
Macro hygiene * is * проблема с областью: если вы хотите глобально известный 'f', тогда вам нужно ... сделать именно это. Я добавил два примера для этого. –