macros - 这些宏有什么区别?

标签 macros scheme chicken-scheme

我对宏在 Scheme 中的工作方式有一些疑问(特别是在 Chicken Scheme 中),让我们考虑这个例子:

(define (when-a condition . body)
  (eval `(if ,condition
     (begin ,@body)
     '())))

(define-syntax when-b
  (er-macro-transformer
    (lambda (exp rename compare)
      (let ((condition (cadr exp))
            (body (cddr exp)))
        `(if ,condition (begin ,@body) '())))))

(define-syntax when-c
  (ir-macro-transformer
    (lambda (exp inject compare)
      (let ((condition (cadr exp))
            (body (cddr exp)))
        `(if ,condition (begin ,@body) '())))))

(define-syntax when-d
  (syntax-rules ()
    ((when condition body ...)
     (if condition (begin body ...) '()))))
  1. 我可以将 when-a 视为一个宏吗?我觉得我不能以严格的方式将其视为宏,因为我没有使用 define-syntax 但我无法说出任何不喜欢此实现的实际理由。

  2. 我的宏是否卫生?

  3. when-bwhen-c 有区别吗?因为我没有使用 renameinject 我认为没有。

最佳答案

Can I consider when-a a macro? I feel that I can't consider it a macro in a strict way since I'm not using define-syntax but I'm not able to say any pratical reason to not prefer this implementation.

这就像一个宏,但它与真正的宏并不完全相同,原因如下:

  • 真正的宏与基于 eval 的“宏”之间的主要区别在于,您的方法将在调用它之前评估其所有参数。这是一个非常重要的区别。例如:(if#f (error "oops") '()) 将评估为 '()(when-a #f (error "oops")) 将引发错误。
  • 这不卫生。在此之前,可能已经做了类似 (eval '(define if "not a procedure")) 之类的操作,这意味着此 eval 将失败; “扩展”正文表达式中的 if 不引用定义站点中的 if
  • 它不会在编译时展开。这是使用宏的另一个主要原因;编译器将扩​​展它,并且在运行时不会执行任何计算来执行扩展。宏本身将完全消失。只剩下扩张。

Are my macros hygienic?

只有 when-cwhen-d 是,因为 ir-macro-transformersyntax 做了保证-规则。在 when-b 中,您必须重命名 ifbegin 以使它们引用 if 的版本并从宏定义站点开始

例子:

(let ((if #f))
  (when-b #t (print "Yeah, ok")))

== expands to ==>

(let ((if1 #f))
  (if1 #t (begin1 (print "Yeah, ok"))))

这会失败,因为 if 的两个版本(这里用额外的 1 后缀注释)指的是同一件事,所以我们最终会调用 #f 在运算符位置。

相比之下,

(let ((if #f))
  (when-c #t (print "Yeah, ok")))

== expands to ==>

(let ((if1 #f))
  (if2 #t (begin1 (print "Yeah, ok"))))

这将按预期工作。如果你想重写 when-b 是卫生的,这样做:

(define-syntax when-b
  (er-macro-transformer
    (lambda (exp rename compare)
      (let ((condition (cadr exp))
            (body (cddr exp))
            (%if (rename 'if))
            (%begin (rename 'begin)))
        `(,%if ,condition (,%begin ,@body) '())))))

请注意额外的 % 前缀标识符,它们引用 ifbegin 的原始值,因为它们位于宏。

Is there any difference between when-b and when-c? Since I'm not using rename nor inject I think there isn't.

有。 隐式 重命名宏被称为是因为它们隐式地重命名来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果您注入(inject)任何标识符,则会撤消此隐式重命名,这会使它们不卫生地可供调用代码捕获。

另一方面,调用显式重命名宏是因为您必须显式重命名任何标识符以防止它们被调用代码捕获。

关于macros - 这些宏有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48156478/

相关文章:

macros - tt 元变量类型在 Rust 宏中意味着什么?

date - SAS宏变量到时间戳的转换

macros - 什么是方案宏?

lisp - 让方案出错

scheme - 是否有与 SBCL 的运行程序等效的方案?

compilation - 我如何编译这个“鸡计划”代码?

lambda - 对Scheme中定义的参数: lambdas vs.的不同处理

C++ 引用计数 : use a Macro to do it, 有什么优点?

c++ - 宏调用中的 #ifdef 适用于 gcc 但不适用于 msvc

list - 使用较长列表中的任意两个元素创建列表 DrRacket