我有一些关于如何在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 ...) '()))))when-a是一个宏吗?我觉得我不能严格地把它看作是一个宏,因为我没有使用define-syntax,但是我无法说出不喜欢这个实现的任何实际原因。when-b和when-c有什么区别吗?由于我没有使用rename或inject,所以我认为没有。发布于 2018-01-08 20:27:11
我能考虑一下什么时候-一个宏吗?我觉得我不能严格地把它看作一个宏,因为我没有使用定义语法,但是我不能说出任何实际的理由不喜欢这个实现。
这类似于宏,但与真正的宏不完全相同,原因如下:
(if #f (error "oops") '())将计算为'(),但(when-a #f (error "oops"))将引发一个错误。(eval '(define if "not a procedure"))的事情,这意味着这一评估将失败;在“展开”的体表达式中的if并不是指定义站点上的if。我的宏卫生吗?
只有when-c和when-d是这样,因为ir-macro-transformer和syntax-rules提供了保证。在when-b中,您必须重命名if和begin,才能使它们引用宏定义站点上的if和begin版本。
示例:
(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) '())))))注意额外的%-prefixed标识符,它引用了if和begin的原始值,因为它们位于宏的定义位置。
时间-b和时间-c之间有什么区别吗?因为我不使用重命名或注入,所以我认为没有。
的确有。隐式重命名宏之所以被调用,是因为它们隐式地重命名了来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果注入任何标识符,则取消此隐式重命名,这使得调用代码从卫生角度上可以捕获这些标识符。
另一方面,调用显式重命名宏是因为您必须显式重命名任何标识符,以防止调用代码捕获它们。
https://stackoverflow.com/questions/48156478
复制相似问题