在Clojure中嵌套语法引号中协调自动生成语法

在Clojure中嵌套语法引号中协调自动生成语法,clojure,gensym,Clojure,Gensym,在Clojure中,您需要使用gensym在宏中创建内部使用的符号,以保持它们的卫生。但是,有时需要在嵌套的语法引号中使用相同的符号。例如,如果我想用let将一个值绑定到一个符号,并在展开的循环中打印三次,我会这样做 `(let [x# 1] ~@(repeat 3 `(println x#))) 但这会产生 (clojure.core/let [x__2__auto__ 1] (clojure.core/println x_

在Clojure中,您需要使用
gensym
在宏中创建内部使用的符号,以保持它们的卫生。但是,有时需要在嵌套的语法引号中使用相同的符号。例如,如果我想用
let
将一个值绑定到一个符号,并在展开的循环中打印三次,我会这样做

`(let [x# 1]
   ~@(repeat 3
             `(println x#)))
但这会产生

(clojure.core/let [x__2__auto__ 1]
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__))
x#
let
表单中生成的符号与嵌套在其中的
println
表单中的符号不同,因为它们是从不同的语法引号中创建的

为了解决这个问题,我可以事先生成符号并将其插入到语法引号中:

(let [x (gensym)]
  `(let [~x 1]
     ~@(repeat 3
               `(println ~x)))
) 
这将产生正确的结果,在所有地方都需要相同的符号:

(clojure.core/let [G__7 1]
                  (clojure.core/println G__7)
                  (clojure.core/println G__7)
                  (clojure.core/println G__7))
现在,虽然它确实产生了正确的结果,但代码本身看起来丑陋而冗长。我不喜欢必须“声明”一个符号,注入语法使它看起来像是来自宏外部,或者是在宏内部的某个地方计算出来的。我希望能够使用auto-gensym语法,它清楚地表明这些是宏内部符号


那么,有没有办法将auto-gensym与嵌套的语法引号一起使用,并使它们产生相同的符号?

auto-gensym的符号仅在定义它们的语法引号内有效,并且它们不在无引号的代码中工作,因为这不是语法引号的一部分

此处符号
x 35;
被其gensym替换,因为它在以下语法引号的范围内:

core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)
core> `(let [x# 1] ~@x#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1) 
如果您取消引用它,它将不再被转换为它的语法quote:

core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)
core> `(let [x# 1] ~@x#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1) 
Auto gensym是语法引号中非常方便的快捷方式,在其他任何地方,您显然需要直接使用
gensym
,后面的示例就是这样

还有其他方法可以构造此宏,因此AutoGenSym可以工作,尽管在Clojure和其他Lisp中,在宏顶部的let中声明gensymed符号是非常正常的。

您的方法(调用gensym)是正确的

但是,在某些情况下,您可以巧妙地使用
doto
->
->
。见:

 `(let [x# 1]
   (doto x#
     ~@(repeat 3 `println)))

谢谢我无法正常引用print表达式,但我想对于更复杂的宏来说,这将变得很麻烦……如果您这样做,我强烈建议使用'println,而不是'println-如果宏用户具有不寻常的名称空间或词汇上下文,则前者不受名称捕获的约束。@IdanArye抱歉,它不起作用,因为我的答案是错误的:-(我添加了一个希望正确的编辑我实际上考虑过使用
doto
,但没有找到命令-我在寻找类似于
dowith
,因为这是我从其他语言中习惯的东西。。。