我有一些关于如何在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
复制相似问题