所以......我是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
您将使用一个保证不会出现在代码中其他任何地方的符号,而不是使用扩展的名称。您可以gensym
在 Common Lisp 中使用。
Paul Graham 的 On Lisp有关于宏的高级材料。
如果您想象一个宏只是简单地扩展到使用它的地方,那么您也可以想象如果您在宏中使用一个变量,那么在使用该a
宏的地方可能已经定义了一个变量a
。
这不是a
你想要的!
这样的事情不可能发生的宏观系统称为卫生系统。
有几种方法可以解决这个问题。一种方法是简单地在宏中使用非常长、非常神秘、非常不可预测的变量名。
一个稍微更精细的版本是gensym
其他一些宏系统使用的方法:代替you,程序员提出一个非常长、非常神秘、非常不可预测的变量名,你可以调用gensym
生成一个非常长、非常为您提供神秘、非常不可预测且唯一的变量名称。
就像我说的,在一个卫生的宏观系统中,这样的冲突一开始就不会发生。如何使宏观系统卫生本身就是一个有趣的问题,Scheme 社区已经在这个问题上花费了几十年的时间,并且他们不断提出越来越好的方法来做到这一点。
我很高兴知道这种语言仍在使用!卫生代码是在注入(通过宏)时不会与现有变量发生冲突的代码。
维基百科上有很多关于此的好信息:http ://en.wikipedia.org/wiki/Hygienic_macro
这是我发现的。解释它的含义完全是另一回事!
http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-ZH-1.html#node_toc_node_sec_12.1
宏转换代码:它们将一段代码转换为其他代码。作为该转换的一部分,他们可能会用更多代码围绕该代码。如果原始代码引用了一个变量a
,并且在它周围添加的代码定义了一个新版本的a
,那么原始代码将无法按预期工作,因为它将访问错误a
:如果
(myfunc a)
是原始代码,期望a
为整数,宏将X
其转换为
(let ((a nil)) X)
然后宏将适用于
(myfunc b)
但(myfunc a)
会变成
(let ((a nil)) (myfunc a))
这不起作用,因为myfunc
将应用于nil
而不是它所期望的整数。
卫生宏通过确保使用的名称是唯一的,避免了访问错误变量的问题(以及相反的类似问题)。
维基百科对卫生宏有很好的解释。
除了提到的所有内容之外,Scheme 的卫生宏还有一件重要的事情,它来自词法范围。
假设我们有:
(syntax-rules () ((_ a b) (+ a b)))
作为宏的一部分,它肯定会插入+,当已经存在+时它也会插入它,但随后是另一个与 具有相同含义的符号+
。它将符号绑定到它们在syntax-rules
谎言的词法环境中的值,而不是它应用的地方,毕竟我们是词法范围的。它很可能会在此处插入一个全新的符号,但该符号全局绑定到与+
定义宏的位置相同的含义。当我们使用如下结构时,这是最方便的:
(let ((+ *))
; piece of code that is transformed
)
因此,宏的编写者或用户不必忙于确保它的使用顺利进行。