我是Racket和Lisp的初学者,正在玩语法定义。我定义了一个像这样的简单转换:
(define-syntax hello
(syntax-rules (in)
((_ name in world) (format "Hello ~a in ~a" name world))
((_ in name) (format "Hello ~a in here" name))))
现在,当我像这两种情况一样运行它时,它运行正常:
(hello "me" in "world") === "Hello me in world"
和
(define in "inside")
(hello me in in) === "Hello me in inside"
但是,这会导致错误,
(let ([in "Shire"])
(hello "Martin" in in)) === Error: hello: bad syntax in: (hello "Martin" in in)
那么,hello在let绑定中失败的原因是什么,而它适用于定义?另外,我在哪里可以获得有关这种差异的更具体信息?感谢。
答案 0 :(得分:4)
这与语法文字的工作方式有关。特别是,在以下情况下,语法文字被视为匹配:
define
案例对您有用,因为您可能将define
放在与宏相同的模块中。这意味着匹配的条件2:in
在宏定义和使用方面具有相同的绑定。但是,如果您在一个模块中定义了宏(define
没有in
),并且在您使用宏的其他模块中定义了in
,那么事情就会赢得。工作得很好。 : - )
let
案例为in
创建了新的约束。这将永远不会匹配顶级宏定义中in
的绑定。
答案 1 :(得分:3)
原因有点微妙 - 既不“应该”有效,但当define
处于顶层时,它实际上改变对{{1}的解释},这是行为产生的地方。
如果您查看the documentation for syntax-rules
,您会发现每个 syntax-rules
的处理方式与syntax-case
中的相同。该文档指出使用literal-id
:
与
free-identifier=?
具有相同绑定的id
匹配语法对象,该语法对象是具有相同绑定的标识符free-identifier=?
。
这意味着文字与绑定匹配,而不是作为基准。也就是说,如果您在literal-id
中有in
的本地定义,则它与let
中标记为文字的in
分开绑定,因此它们不会比赛。此外,在单独模块中使用syntax-rules
引入的标识符也不匹配。
有趣的是,如果define
从定义宏的模块导出,然后使用in
导入,则重命名的绑定仍然有效,因为Racket会跟踪重命名并认为它们是相同的绑定即使它们不是同名。这些都是正交的概念,即使它有时很容易忽视这个想法的所有细微差别,但它有点内在于卫生。
回到相关示例,为什么以下工作?
rename-in
嗯,定义上下文是相互递归,因此宏定义后(define-syntax hello
(syntax-rules (in)
((_ name in world) (format "Hello ~a in ~a" name world))
((_ in name) (format "Hello ~a in here" name))))
(define in "inside")
(hello "me" in in)
的定义实际上被in
选中。因此,syntax-rules
宏实际上需要与hello
相同的绑定,因此调用有效。但是,如果in
在与hello
不同的模块中定义,则宏调用将不,因为绑定会有所不同:
in