所以...我是计划r6rs的新手,我正在学习宏。有人可以向我解释“卫生”是什么意思吗?
提前致谢。
答案 0 :(得分:22)
卫生通常用于宏的环境中。卫生宏不使用可能存在干扰扩展代码的变量名称。这是一个例子。假设我们想用宏定义or
特殊形式。直观地,
(or a b c ... d)
会扩展为(let ((tmp a)) (if tmp a (or b c ... d)))
。 (为简单起见,我省略了空的(or)
案例。)
现在,如果名称tmp
实际上已添加到代码中,就像上面描绘的扩展一样,它将不卫生,而且很糟糕,因为它可能会干扰另一个具有相同名称的变量。说,我们想评估
(let ((tmp 1)) (or #f tmp))
使用我们的直观扩展,这将成为
(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))
宏中的tmp
会影响最外层的tmp
,因此结果为#f
而不是1
。
现在,如果宏是卫生的(并且在Scheme中,使用syntax-rules
时会自动出现这种情况),那么您将使用符号来代替使用名称tmp
进行扩展。保证不会出现在代码中的任何其他位置。您可以在Common Lisp中使用gensym
。
Paul Graham's On Lisp在宏上有高级材料。
答案 1 :(得分:7)
如果您认为宏只是扩展到使用它的位置,那么您还可以想象如果在宏中使用变量a
,则可能已经 在使用该宏的位置定义的变量a
。
这是不你想要的a
!
不能发生此类事情的宏系统被称为 hygienic 。
有几种方法可以解决这个问题。一种方法是在宏中使用非常长,非常神秘,非常难以预测的变量名称。
稍微更精确的版本是其他一些宏系统使用的gensym
方法:代替你,程序员提出了一个非常长,非常神秘,非常不可预测的方法变量名称,你可以调用gensym
函数,它为你生成一个非常长,非常神秘,非常不可预测和唯一的变量名 。
就像我说的那样,在一个卫生的宏观系统中,这种碰撞不可能首先发生。 如何使宏观系统卫生本身就是一个有趣的问题,Scheme社区已经花费了几十年的时间来研究这个问题,他们不断提出更好,更好的方法。
答案 2 :(得分:3)
我很高兴知道这种语言仍在使用中!卫生代码是注入时(通过宏)不会导致与现有变量发生冲突的代码。
维基百科上有很多关于此的信息:http://en.wikipedia.org/wiki/Hygienic_macro
答案 3 :(得分:2)
这是我发现的。解释它的含义完全是另一回事!
http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-1.html#node_toc_node_sec_12.1
答案 4 :(得分:2)
宏转换代码:它们需要一些代码并将其转换为其他代码。作为转换的一部分,他们可以使用更多代码包围该代码。如果原始代码引用变量a
,并且在其周围添加的代码定义了a
的新版本,则原始代码将无法按预期工作,因为它将访问错误的{{ 1}}:if
a
是原始代码,期望(myfunc a)
为整数,宏取a
并将其转换为
X
然后宏将适用于
(let ((a nil)) X)
但(myfunc b)
将转换为
(myfunc a)
不起作用,因为(let ((a nil)) (myfunc a))
将应用于myfunc
而不是预期的整数。
卫生宏通过确保使用的名称是唯一的,避免了访问错误变量的问题(反之亦然的类似问题)。
维基百科对hygienic macros有很好的解释。
答案 5 :(得分:2)
除了上面提到的所有内容之外,Scheme的卫生宏还有一个重要的东西,它来自词法范围。
说我们有:
(syntax-rules () ((_ a b) (+ a b)))
作为宏的一部分,它肯定会插入+,当它已经存在+时它也会插入它,但是然后是另一个与+
具有相同含义的符号。它将符号绑定到它们在syntax-rules
所在的词汇环境中所具有的值,而不是它应用的位置,我们毕竟是词法范围的。它很可能会在那里插入一个全新的符号,但是与+
具有相同含义的全局符号位于定义宏的位置。当我们使用如下构造时,这是最方便的。
(let ((+ *))
; piece of code that is transformed
)
因此,作者或宏的用户无需担心确保其使用顺利。