在诡计方案中,如何防止从另一个宏调用宏时重命名?

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来定义alambdaapply-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))))))