那么……我是scheme r6rs的新手,正在学习宏。有人能解释一下什么是“卫生”吗?
提前感谢。
那么……我是scheme r6rs的新手,正在学习宏。有人能解释一下什么是“卫生”吗?
提前感谢。
卫生通常在宏的上下文中使用。一个卫生的宏不使用变量名,以免干扰扩展代码。这里有一个例子。假设我们想用宏定义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的《On Lisp》有关于宏的高级材料。
(let ((if "bleh")) (my-macro))
,这个问题不能仅通过gensym来解决。 - Eli Barzilay(myfunc a)
这是原始代码,期望 a
是一个整数,而宏将取 X
并将其转换为
(let ((a nil)) X)
(myfunc b)
但是(myfunc a)
将会被转换为
(let ((a nil)) (myfunc a))
这样做行不通,因为myfunc
将被应用于nil
而不是它所期望的整数。
卫生宏通过确保使用的名称是唯一的来避免访问错误变量的问题(以及相反方向的类似问题)。
维基百科对卫生宏有很好的解释。
(syntax-rules () ((_ a b) (+ a b)))
作为宏的一部分,它肯定会插入 +,如果已经有 + 了,它也会插入另一个具有与+
相同含义的符号。它将符号绑定到它们在语法规则所在的词法环境中的值,并不是应用它的位置,毕竟我们是词法作用域。它很可能会在那里插入一个全新的符号,但该符号与在定义宏的地方全局绑定的+
相同。当我们使用这样的结构时,它非常方便:
(let ((+ *))
; piece of code that is transformed
)
因此,编写宏的作者或用户无需担心其使用是否顺利。
(let ((if +)) ...)
这样的代码没有严重失败,直到我意识到这是因为词法作用域。 - Zorf