在诡计方案中,如何防止从另一个宏调用宏时重命名?
In guile scheme, how to prevent renaming when calling macro from another macro?
; Having this definition that creates identifier `self'
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda lambda-list . body)
(with-syntax ([name (datum->syntax #'alambda 'self)])
#'(letrec ([name (lambda lambda-list . body)])
name))])))
; I want to "compose" it with another macro
(define-syntax-rule [apply-alambda args argv . body]
((alambda args . body) . argv))
; But then it doesn't work (while alambda itself does)
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
; => Unbound variable: self
; (expected 120)
如何防止apply-alambda
重命名self
?
我尝试使用 define-macro
也没有用,但原因不同:
(defmacro apply-alambda [args argv . body]
((alambda args . body) . argv))
; => lambda: bad lambda in form (lambda args . body)
这里,我都不知道哪里出了问题
您的 alambda
宏不卫生,而且不卫生的宏不能很好地组合。基于已用于其他目的的子术语创建标识符的不卫生宏组合特别糟糕。一种解决方案是创建一个辅助宏,将新标识符的 "lexical context" 作为单独的参数。然后从中创建派生宏。
(define-syntax alambda/lctx
(lambda (stx)
(syntax-case stx ()
[(alambda lctx formals . body)
(with-syntax ([name (datum->syntax #'lctx 'self)])
#'(letrec ([name (lambda formals . body)])
name))])))
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda formals . body)
#'(alambda/lctx alambda formals . body)])))
(define-syntax apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
#'((alambda/lctx apply-alambda formals . body) . argv)])))
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
在 alambda
宏中,用于创建 self
活页夹的词法上下文取自对宏本身的引用。宏在对 alambda/lctx
的调用中使该参数 显式 。与 apply-alambda
类似——如果您想创建另一个扩展为 apply-alambda
的宏,那么您同样应该创建一个 apply-alambda/lctx
助手。
(在 Racket 中,词法上下文不仅附加到标识符,还附加到列表结构("parentheses"),不卫生的宏使用整个语法对象是很常见的,如 (datum->syntax stx 'self)
。这避免了对单独的辅助宏的需要。)
注意:使用define-syntax-rule
来定义alambda
和apply-alambda
是行不通的,因为它实际上并没有在运算符位置绑定标识符。
您可能会想 apply-alambda
使用 alambda
标识符调用 alambda
,其词法上下文对应于 apply-alambda
形式的使用,如下所示:
(define-syntax bad-apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
(with-syntax ([alambda (datum->syntax #'apply-alambda 'alambda)])
#'((alambda formals . body) . argv))])))
此版本错误。如果 alambda
未在使用 bad-apply-alambda
的范围内绑定(或绑定到错误的对象),则其行为不正确。例如:
(let ([alambda 5])
(bad-apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1))))))
; Having this definition that creates identifier `self'
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda lambda-list . body)
(with-syntax ([name (datum->syntax #'alambda 'self)])
#'(letrec ([name (lambda lambda-list . body)])
name))])))
; I want to "compose" it with another macro
(define-syntax-rule [apply-alambda args argv . body]
((alambda args . body) . argv))
; But then it doesn't work (while alambda itself does)
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
; => Unbound variable: self
; (expected 120)
如何防止apply-alambda
重命名self
?
我尝试使用 define-macro
也没有用,但原因不同:
(defmacro apply-alambda [args argv . body]
((alambda args . body) . argv))
; => lambda: bad lambda in form (lambda args . body)
这里,我都不知道哪里出了问题
您的 alambda
宏不卫生,而且不卫生的宏不能很好地组合。基于已用于其他目的的子术语创建标识符的不卫生宏组合特别糟糕。一种解决方案是创建一个辅助宏,将新标识符的 "lexical context" 作为单独的参数。然后从中创建派生宏。
(define-syntax alambda/lctx
(lambda (stx)
(syntax-case stx ()
[(alambda lctx formals . body)
(with-syntax ([name (datum->syntax #'lctx 'self)])
#'(letrec ([name (lambda formals . body)])
name))])))
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda formals . body)
#'(alambda/lctx alambda formals . body)])))
(define-syntax apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
#'((alambda/lctx apply-alambda formals . body) . argv)])))
(apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1)))))
在 alambda
宏中,用于创建 self
活页夹的词法上下文取自对宏本身的引用。宏在对 alambda/lctx
的调用中使该参数 显式 。与 apply-alambda
类似——如果您想创建另一个扩展为 apply-alambda
的宏,那么您同样应该创建一个 apply-alambda/lctx
助手。
(在 Racket 中,词法上下文不仅附加到标识符,还附加到列表结构("parentheses"),不卫生的宏使用整个语法对象是很常见的,如 (datum->syntax stx 'self)
。这避免了对单独的辅助宏的需要。)
注意:使用define-syntax-rule
来定义alambda
和apply-alambda
是行不通的,因为它实际上并没有在运算符位置绑定标识符。
您可能会想 apply-alambda
使用 alambda
标识符调用 alambda
,其词法上下文对应于 apply-alambda
形式的使用,如下所示:
(define-syntax bad-apply-alambda
(lambda (stx)
(syntax-case stx ()
[(apply-alambda formals argv . body)
(with-syntax ([alambda (datum->syntax #'apply-alambda 'alambda)])
#'((alambda formals . body) . argv))])))
此版本错误。如果 alambda
未在使用 bad-apply-alambda
的范围内绑定(或绑定到错误的对象),则其行为不正确。例如:
(let ([alambda 5])
(bad-apply-alambda [x] [5] (if (= 0 x) 1 (* x (self (- x 1))))))