ラケットでマクロを作成しようとしています。これは、のヘッダー、つまり部分だけdefine-let
を「保存」し、後で再利用できるようにします。(let ((var value) ...) ...)
(var value) ...
以下のコードは期待どおりに動作します。
#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".
問題は、マクロが健全性を損なうことです: 以下の例に示すように、マクロによって生成される ay
で宣言された変数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
最初の例のように動作するだけでなく、2 番目の例のように、マクロによって識別子が生成されたときに衛生状態を維持するようにマクロを作成するにはどうすればよい"okay"
でしょうか?