在 Scheme 中捕获宏 [英] Capturing Macros in Scheme

查看:26
本文介绍了在 Scheme 中捕获宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

作为一个具体的例子,这里是一个 CL 风格的宏系统中的琐碎的 aif.

(defmacro aif (test if-true & optional if-false)`(让((它,测试))(如果是,如果为真,如果为假)))

想法是 it 将在 if-trueif-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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆