Java Clojure:交叉引用变量、声明和编译错误

Java Clojure:交叉引用变量、声明和编译错误,java,clojure,Java,Clojure,在名称空间中,我定义了两个变量(以及其他变量),它们是映射: (declare bar) (def foo {:is-related-to bar}) (def bar {:is-related-to foo}) 因为定义foo时bar不存在,所以我使用(declare bar)向前声明它 到目前为止没有任何问题,REPL中的一切都按照预期工作 我们注意到的唯一一件事是,当我在REPL中检查foo时,我看到bar是未绑定的,我认为使用declare可以预料到这一点: #<Unboun

在名称空间中,我定义了两个变量(以及其他变量),它们是映射:

(declare bar)

(def foo {:is-related-to bar})

(def bar {:is-related-to foo})
因为定义
foo
bar
不存在,所以我使用
(declare bar)
向前声明它

到目前为止没有任何问题,REPL中的一切都按照预期工作

我们注意到的唯一一件事是,当我在REPL中检查
foo
时,我看到
bar
是未绑定的,我认为使用
declare
可以预料到这一点:

#<Unbound Unbound: #'user/bar>
我认为这也是意料之中的,因为我认为编译器不能处理未绑定的变量


在任何情况下,如果所有这些行为都是可以预期的,那么如果不能编译,为什么人们要使用转发声明呢?我可能在这里遗漏了什么。

您不能以这种方式构造循环引用。它不起作用的原因是
def
计算作为绑定传递的表单,而计算解析为Var的符号会得到该Var的当前绑定。换句话说,映射
foo
中的内容不是对Var
bar
的引用,而是
bar
的值。在事实发生后重新定义
bar
不会影响
foo
的值-这就是Clojure中的不变性点

转发声明通常用于允许函数之间的循环依赖关系。下面的方法是有效的,因为函数体在函数实际被调用之前不会被计算;定义函数时,它实际上是对编译的Var的引用

 (declare bar)

 (defn foo [x y]
   (bar x (* 2 y)))

 (defn bar
   ([x] (foo x 3))
   ([x y] (+ x y)))

如果要引用符号栏,应使用#'栏。使用bar时,bar的值尚未定义。如果您使用#条形图,则表示可以在需要时计算并正确定义的条形图符号

(declare bar)

(def foo {:is-related-to #'bar})

(def bar {:is-related-to #'foo})

user=> foo
{:is-related-to #'user/bar}
user=> (:is-related-to foo)
#'user/bar

这应该可以编译。您使用的是什么版本的Clojure?顺便说一句,定义bar不会改变foo将被映射为Unbound的事实,因为在定义
foo
时,
bar
将被计算。映射指向变量的值,而不是变量本身。@DiegoBasch我使用的是1.6.0。关于您的评论:没错,这就是为什么我总是看到
。#我怀疑您看到的异常是由于其他一些问题造成的,例如将
foo
作为参数传递给
eval
。是的,但我真的希望得到变量的值,而不是对其符号的引用,所以我没有使用该方法。然而,根据所有这些信息,我将检查这是否仍然有效。谢谢
#“
var
的读取器宏。e、 g.#'bar==(var bar)因此,当您使用#'bar时,实际上会将var绑定到symbol
bar
(而不是符号本身)。为了澄清我之前的评论,使用这样的var仍然“有效”,但它并不等同于Alex上面的答案,因为您在地图中使用vars,并且需要使用@/deref来查看它们的值。
(declare bar)

(def foo {:is-related-to #'bar})

(def bar {:is-related-to #'foo})

user=> foo
{:is-related-to #'user/bar}
user=> (:is-related-to foo)
#'user/bar