这些宏有什么区别?
What is the difference between these macros?
我对宏在 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 ...) '()))))
我可以考虑when-a
一个宏吗?我觉得我不能严格地认为它是一个宏,因为我没有使用 define-syntax
但我不能说出任何不喜欢这个实现的实际理由。
我的宏卫生吗?
when-b
和when-c
有区别吗?因为我既没有使用 rename
也没有使用 inject
我认为没有。
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 的 "macro" 之间的主要区别在于,您的方法将在 调用它之前 评估其所有参数。这是一个非常重要的区别。例如:
(if #f (error "oops") '())
将评估为 '()
但 (when-a #f (error "oops"))
将引发错误。
- 不卫生。在此之前,有人可能做了类似
(eval '(define if "not a procedure"))
的操作,这意味着此 eval 将失败; "expansion" 正文表达式中的 if
未引用定义站点中的 if
。
- 它不会在编译时展开。这是使用宏的另一个主要原因;编译器将扩展它,并且在运行时不会执行任何计算来执行扩展。宏本身将完全消失。只剩下扩展了。
Are my macros hygienic?
只有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) '())))))
请注意额外的 %
前缀标识符,它们引用 if
和 begin
的原始值,因为它们位于宏定义的位置。
Is there any difference between when-b and when-c? Since I'm not using rename nor inject I think there isn't.
有。 隐式 重命名宏被称为是因为它们隐式地重命名来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果您注入任何标识符,则会撤消此隐式重命名,这会使它们不卫生地可供调用代码捕获。
另一方面,显式 重命名宏被调用是因为您必须显式重命名任何标识符以防止它们被调用代码捕获。
我对宏在 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 ...) '()))))
我可以考虑
when-a
一个宏吗?我觉得我不能严格地认为它是一个宏,因为我没有使用define-syntax
但我不能说出任何不喜欢这个实现的实际理由。我的宏卫生吗?
when-b
和when-c
有区别吗?因为我既没有使用rename
也没有使用inject
我认为没有。
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 的 "macro" 之间的主要区别在于,您的方法将在 调用它之前 评估其所有参数。这是一个非常重要的区别。例如:
(if #f (error "oops") '())
将评估为'()
但(when-a #f (error "oops"))
将引发错误。 - 不卫生。在此之前,有人可能做了类似
(eval '(define if "not a procedure"))
的操作,这意味着此 eval 将失败; "expansion" 正文表达式中的if
未引用定义站点中的if
。 - 它不会在编译时展开。这是使用宏的另一个主要原因;编译器将扩展它,并且在运行时不会执行任何计算来执行扩展。宏本身将完全消失。只剩下扩展了。
Are my macros hygienic?
只有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) '())))))
请注意额外的 %
前缀标识符,它们引用 if
和 begin
的原始值,因为它们位于宏定义的位置。
Is there any difference between when-b and when-c? Since I'm not using rename nor inject I think there isn't.
有。 隐式 重命名宏被称为是因为它们隐式地重命名来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果您注入任何标识符,则会撤消此隐式重命名,这会使它们不卫生地可供调用代码捕获。
另一方面,显式 重命名宏被调用是因为您必须显式重命名任何标识符以防止它们被调用代码捕获。