Я пытаюсь написать макрос define-let
в racket, который« сохраняет »заголовок (let ((var value) ...) ...)
, а именно только часть (var value) ...
, и позволяет повторно использовать его позже ,Написание макроса `define-let`, с гигиеникой
Приведенный ниже код работает, как ожидалось:
#lang racket
;; define-let allows saving the header part of a let, and re-use it later
(define-syntax (define-let stx1)
(syntax-case stx1()
[(_ name [var value] ...)
#`(define-syntax (name stx2)
(syntax-case stx2()
[(_ . body)
#`(let ([#,(datum->syntax stx2 'var) value] ...)
. body)]))]))
;; Save the header (let ([x "works]) ...) in the macro foo
(define-let foo [x "works"])
;; Use the header, should have the same semantics as:
;; (let ([x "BAD"])
;; (let ([x "works])
;; (displayln x))
(let ([x "BAD"])
(foo (displayln x))) ;; Displays "works".
Проблема заключается в том, что макро-брейки гигиены: как показано в приведенном ниже примере, переменная y
, объявленный в define-let
который производится с помощью макроса , должен быть новым, неинтерминированным символом из-за гигиены, но ему удается просачиваться из макроса, и он ошибочно доступен в (displayln y)
.
;; In the following macro, hygiene should make y unavailable
(define-syntax (hygiene-test stx)
(syntax-case stx()
[(_ name val)
#'(define-let name [y val])]))
;; Therefore, the y in the above macro shouldn't bind the y in (displayln y).
(hygiene-test bar "wrong")
(let ((y "okay"))
(bar (displayln y))) ;; But it displays "wrong".
Как я могу написать define-let
макрос так, что он ведет себя, как и в первом примере, но и сохраняет гигиену, когда идентификатор генерируется макросом, давая "okay"
во втором примере?
Если я правильно понял ваши случаи использования, вам нужно использовать параметры, которые устанавливают «рамка» для перегруженной 'x': так в первом случае, вы бы установить параметр, который находится в области видимости ваш вызов 'define-let' верхнего уровня, но будет недоступен для вашего макросочетания -define-let'. Поскольку вы привязываете данные, а не синтаксис для своего 'x', вы должны использовать обычные параметры, но Racket также предоставляет синтаксические параметры для случаев, которые ему нужны. –
@ ChrisJester-Young Я попытался использовать параметр define-syntax, но тогда привязка, которую он вводит, доступна везде, а не только внутри (bar ...). Как я мог избежать этого? –
Это технически доступно на верхнем уровне (при условии, что здесь задан ваш синтаксический параметр), но если вы сделаете свой вызов по умолчанию 'raise-syntax-error' или тому подобное * во время фазы макрорасширения *, это может также быть таким же, как и не существующим. –