我试图在球拍中写一个define-let
宏,它可以“保存”(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-parameter,但随后引入的绑定可以随处使用,而不仅限于(bar ...)部分。我怎么能避免这种情况? –
它在技术层面上是可用的(假设这是你的语法参数被定义的地方),但是如果你在宏扩展阶段*使它的默认绑定调用为'raise-syntax-error' *,那么它也可能与未存在的相同。 –