这些宏有什么区别?

时间:2018-01-08 18:57:34

标签: macros scheme chicken-scheme

关于宏如何在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,我认为没有。

1 个答案:

答案 0 :(得分:3)

  

我可以考虑何时 - 一个宏?我觉得我不能严格地将它视为一个宏,因为我没有使用define-syntax,但是我不能说任何不喜欢这种实现的实际理由。

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

  • 真正的宏与基于eval的"宏之间的主要区别"是你的方法会在之前评估它的所有参数。这是一个非常重要的区别。例如:(if #f (error "oops") '())将评估为'(),但(when-a #f (error "oops"))会引发错误。
  • 不卫生。事先,人们可能会做类似(eval '(define if "not a procedure"))之类的事情,这意味着这个eval会失败; "扩展"的正文表达式中的if不会在定义网站上引用if
  • 它不会在编译时扩展。这是使用宏的另一个主要原因;编译器将对其进行扩展,并且在运行时不会执行任何计算来执行扩展。宏本身将彻底蒸发。只剩下扩张了。
  

我的宏是否卫生?

由于when-cwhen-d提供的保证,

ir-macro-transformersyntax-rules。在when-b中,您必须重命名ifbegin,以便在宏定义网站上引用ifbegin的版本。

示例:

(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的原始值,因为它们位于宏的定义位置。

  

when-b和when-c之间有什么区别吗?由于我不使用重命名或注入,我认为没有。

有。 隐式重命名宏被调用,因为它们隐式重命名来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果您注入任何标识符,则撤消此隐式重命名,这使得它们不受干扰地被调用代码捕获。

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