在Freenode的#scheme频道上提出了一个很好的问题。在Scheme中考虑以下代码:
(define alpha 1)
(define-syntax foo
(syntax-rules (quote alpha)
((_ alpha msg) (define bar 2))
((_ other msg) (syntax-error msg)) ) )
(define (beta)
(foo alpha "beta")
(define alpha 3)
'beta )
(define (gamma)
(define alpha 4)
(foo alpha "gamma")
'gamma )
(define (delta alpha)
(foo alpha "delta")
'delta )
beta
、gamma
和delta
中的哪一个应该产生语法错误?哪个做?我已经用赤壁方案检查了这一点,其中beta
很好,而gamma
和delta
失败。我不知道这是有意为之,还是赤壁的一个bug。
根据该标准,似乎应该在内部定义重写为letrec*
之前扩展宏。因此beta
和gamma
都应该失败,因为foo
将与内部定义的alpha
匹配,而不是全局的。
然而,标准中并没有明确规定内部定义的实际工作方式,只是它们可以被视为letrec快捷方式。我对Racket的R5RS也有同样的行为,所以我似乎在标准中缺少了一些要求这种行为的东西。
好的,我终于明白你的问题了。运行您的代码很有挑战性,因为您似乎有一个"语法错误"函数,只有当它最终以完全展开的代码结束时,它才会发出语法错误的信号。无论什么
我认为你的问题的答案是:
这些Scheme的家伙(Dybvig、Felleisen、Hieb、Clinger、Rees、Wand、Flatt、Culpepper等)都很聪明!
特别是,Scheme/Racket设法弄清楚绑定结构是如何工作的,即使它不知道什么将是绑定或不是绑定。你说得对!太疯狂了!但Dybvig等人概述的算法做了一些非常聪明的事情,以确保卫生跟踪标识符是"自由标识符相等"还是"绑定标识符相等"(Flatt的术语),即使它还不知道哪一个绑定另一个。我个人建议阅读"一起工作的宏"(Flatt,Culpepper,Darais,Findler),以更好地理解这一点。
如果我误解了你的问题,或者我的语气不恰当,请道歉!
可能有点太多,这取决于实现端,但这种行为是因为宏扩展的顺序。从理论上讲,所有的定义都包含alpha
,所以它不应该和文字关键字中的定义相匹配。然而,在将define
形式扩展到letrec*
之前,需要进行宏扩展,否则编译器无法正确检测内部定义。所以在那个时刻,编译器可能看到绑定,也可能看不到绑定。(宏扩展时序是,而不是R7RS上指定的,因此实现也可以选择自己的时序。)
对于beta
的情况,编译器没有捕捉到绑定,所以宏扩展器仍然可以看到alpha
是与全局绑定相同的绑定。其他情况则相反。
首先,delta
已退出(不应与alpha
匹配),因为它在词汇上清楚地将alpha
绑定到另一个绑定,而不是出现sytnax-rules
的绑定。有趣的是CCD_ 21和CCD_。
根据第5.2.2节。R4RS(第13页)和R5RS(第16页),第5.3.2节。R7RS(第26页),以及第11.3节。在R6RS(第32页)中,通过内部定义建立的结合的"区域"是该定义出现的整个<body>
。对foo
的宏调用显然与那些内部定义在相同的<body>
中。
R7RS还进一步警告我们:
请注意,这样的主体[即包含内部定义的主体]可能在扩展其他语法之后才会显现出来。
因此,混乱的事件顺序被承认,但没有歧义;如果在调用宏的同一<body>
中有任何内部定义对alpha
的绑定,则syntax-rules
不应与alpha
分支匹配因此,beta
和gamma
也出局
附录A
如果我们进一步使情况复杂化,并且您的宏本身有条件地绑定alpha
,就像一样
(syntax-rules (alpha)
((_ alpha x) (define alpha x)))
乍一看,这似乎真的很模糊,但我相信这是通过以下事实解决的:宏扩展器将根据卫生条件重命名定义的alpha
标识符,这意味着我们不会将匹配的alpha
隐藏为文字,所以匹配它是可以的,上面的操作只会为重命名的alpha
创建一个绑定,该绑定在宏体之外是不可访问的。
附录B
第5.3节末尾有一个限制。R5RS(第17页),第5.4节末尾。R7RS(第26页),以及第10节的中间部分。在R6RS(第30页)中,提到一系列定义不应包含改变其中任何定义含义的定义。(这实际上有点复杂,所有三个标准都使用了不同的措辞,但这应该是一个合理的总结。)
在您的例子中,我不清楚您的syntax-rules
扩展为语法错误的可能性是否算作其"含义"的歧义。如果认为这是歧义,那么根据R5RS和R7RS,您的beta
和gamma
是"错误"(未定义行为),根据R6RS,则是"语法违规"。
如果您的示例在syntax-rules
的第二个分支中包含另一个绑定(理想情况下甚至是同一变量的定义),那么这个挑剔的问题将不适用,因此您的问题仍然存在。