在 Scheme 中捕获宏 [英] Capturing Macros in Scheme
问题描述
在 Racket 中使用 define-syntax
或 define-syntax-rule
定义捕获宏的最简单方法是什么?
作为一个具体的例子,这里是一个 CL 风格的宏系统中的琐碎的 aif
.
(defmacro aif (test if-true & optional if-false)`(让((它,测试))(如果是,如果为真,如果为假)))
想法是 it
将在 if-true
和 if-false<中绑定到
test
的结果/code> 子句.天真的音译(减去可选的替代)是
(define-syntax-rule (aif test if-true if-false)(让((它测试))(如果它如果-真如果-假)))
它会毫无怨言地进行评估,但如果您尝试在子句中使用 it
则会出错:
<代码>>(aif "Something" (displayln it) (displayln "Nope")))对未定义标识符的引用:它
anaphora
egg 实现 aif
作为
(定义语法 aif(IR-宏变压器(lambda(表单注入比较?)(让((它(注入'它)))(让((测试(cadr形式))(结果(caddr 形式))(替代(cdddr 形式)))(如果(空?替代)`(让((,它,测试))(如果,它,结果))`(让((,它,测试))(如果,它,结果,(汽车替代)))))))))
但 Racket 似乎没有定义或记录 ir-macro-transformer
.
默认情况下,Racket 宏旨在避免捕获.当您使用 define-syntax-rule
时,它将尊重词法范围.
当你想故意破坏卫生"时,传统上在 Scheme 中你必须使用 syntax-case
并且(小心地)使用 datum->syntax
.p>
但在 Racket 中,执行照应"宏最简单、最安全的方法是使用 语法参数和简单的define-syntax-rule
.
例如:
(需要球拍/stxparam)(定义语法参数它(拉姆达(stx)(raise-syntax-error (syntax-e stx)"只能在aif内部使用")))(定义语法规则(aif 条件 true-expr false-expr)(让 ([tmp 条件])(如果 tmp(语法参数化([它(make-rename-transformer #'tmp)])真-表达式)假表达式)))
我写了关于语法参数的文章here 你也应该阅读 EliBarzilay 的 肮脏的卫生 博文a> 和 使用语法参数保持整洁 论文 (PDF).
What's the simplest way to define a capturing macro using define-syntax
or define-syntax-rule
in Racket?
As a concrete example, here's the trivial aif
in a CL-style macro system.
(defmacro aif (test if-true &optional if-false)
`(let ((it ,test))
(if it ,if-true ,if-false)))
The idea is that it
will be bound to the result of test
in the if-true
and if-false
clauses. The naive transliteration (minus optional alternative) is
(define-syntax-rule (aif test if-true if-false)
(let ((it test))
(if it if-true if-false)))
which evaluates without complaint, but errors if you try to use it
in the clauses:
> (aif "Something" (displayln it) (displayln "Nope")))
reference to undefined identifier: it
The anaphora
egg implements aif
as
(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)))))))))
but Racket doesn't seem to have ir-macro-transformer
defined or documented.
Racket macros are designed to avoid capture by default. When you use define-syntax-rule
it will respect lexical scope.
When you want to "break hygiene" intentionally, traditionally in Scheme you have to use syntax-case
and (carefully) use datum->syntax
.
But in Racket the easiest and safest way to do "anaphoric" macros is with a syntax parameter and the simple define-syntax-rule
.
For example:
(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)))
I wrote about syntax parameters here and also you should read Eli Barzilay's Dirty Looking Hygiene blog post and Keeping it Clean with Syntax Parameters paper (PDF).
这篇关于在 Scheme 中捕获宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!