有人可以向我解释“卫生”的概念(我是计划程序员)吗?

时间:2010-06-09 22:36:33

标签: macros scheme r6rs hygiene

所以...我是计划r6rs的新手,我正在学习宏。有人可以向我解释“卫生”是什么意思吗?

提前致谢。

6 个答案:

答案 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
)

因此,作者或宏的用户无需担心确保其使用顺利。