在Scheme中捕获宏

Ina*_*thi 6 macros scheme define-syntax racket

使用define-syntaxdefine-syntax-rule在Racket中定义捕获宏的最简单方法是什么?

作为一个具体的例子,这里aif是CL风格的宏系统中的微不足道的.

(defmacro aif (test if-true &optional if-false)
    `(let ((it ,test))
        (if it ,if-true ,if-false)))
Run Code Online (Sandbox Code Playgroud)

这个想法是,it将被绑定到的结果testif-trueif-false条款.天真的音译(减去可选的替代方案)是

(define-syntax-rule (aif test if-true if-false)
    (let ((it test))
       (if it if-true if-false)))
Run Code Online (Sandbox Code Playgroud)

在没有投诉的情况下进行评估,但如果您尝试it在子句中使用则会出错:

> (aif "Something" (displayln it) (displayln "Nope")))
reference to undefined identifier: it
Run Code Online (Sandbox Code Playgroud)

anaphora实现aif

(define-syntax aif
  (ir-macro-transformer
   (lambda (form inject compare?)
     (let ((it (inject 'it)))
       (let ((test (cadr form))
         (consequent (caddr form))
         (alternative (cdddr form)))
     (if (null? alternative)
         `(let ((,it ,test))
        (if ,it ,consequent))
         `(let ((,it ,test))
        (if ,it ,consequent ,(car alternative)))))))))
Run Code Online (Sandbox Code Playgroud)

但是,Racket似乎没有ir-macro-transformer定义或记录.

Gre*_*ott 10

Racket宏旨在避免默认捕获.当你使用define-syntax-rule它时会尊重词汇范围.

当你想故意"打破卫生"时,传统上你需要使用syntax-case和(小心)使用datum->syntax.

但在Racket中,执行"照应"宏的最简单,最安全的方法是使用语法参数和简单define-syntax-rule.

例如:

(require racket/stxparam)

(define-syntax-parameter it
  (lambda (stx)
    (raise-syntax-error (syntax-e stx) "can only be used inside aif")))

(define-syntax-rule (aif condition true-expr false-expr)
  (let ([tmp condition])
    (if tmp
        (syntax-parameterize ([it (make-rename-transformer #'tmp)])
          true-expr)
        false-expr)))
Run Code Online (Sandbox Code Playgroud)

在这里写了关于语法参数的文章,你也应该阅读Eli Barzilay的Dirty Looking Hygiene博客文章用语法参数保持清洁(PDF).

  • TBH,博客文章并不是真正相关的,因为本文更好地涵盖了它,并有更多的例子...... (2认同)
  • @ ThrowawayAccount3Million作为最后一段[这里](http://blog.racket-lang.org/2008/02/dirty-looking-hygiene.html)解释说:"由此产生的宏不会破坏卫生.例如,`( let([it 3])(如果#t it))`计算为'3`,因为它会影响'if`变化的全局`it`.这是一个真正的不卫生宏的变化 - 但这就是整点:我们(宏作者)不会干扰用户代码中的范围." (2认同)
  • 当你真正需要捕获或者为了捕获而想要捕获时,你可以像我提到的那样使用`datum-> syntax`.有了它,一切皆有可能,无论好坏.但要备份:我不会将`aif`用于实际代码.我认为更好的想法就像`if-let`,你提供id.通常,当宏的用户提供标识符时,它更清晰,更可靠.(显然有例如`struct`,但至少会引入以用户提供的id为前缀的名称.) (2认同)