Clojure:交叉引用的变量,声明和编译错误

Neo*_*mov 2 java clojure

在命名空间中,我定义了两个vars(以及其他)是map:

(declare bar)

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

(def bar {:is-related-to foo})
Run Code Online (Sandbox Code Playgroud)

因为bar我定义时不存在foo,所以我正在使用它进行声明(declare bar).

到目前为止没有问题,一切都在REPL中按预期工作.

我们唯一注意到的是,当我检查fooREPL时,我发现它bar是未绑定的,我认为可以通过以下方式使用declare:

#<Unbound Unbound: #'user/bar>
Run Code Online (Sandbox Code Playgroud)

当我尝试用lein jar或编译软件时出现问题lein ring war(因为它是一个Ring应用程序).我从编译器得到的错误是:

Exception in thread "main" java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: Unbound: #'user/bar, compiling...
Run Code Online (Sandbox Code Playgroud)

我认为这也是预料之中的,因为我认为编译器不能处理未绑定的变量.

在任何情况下,如果要预期所有这些行为,如果无法编译,为什么人们使用前向声明?我可能在这里遗漏了一些东西.

Ale*_*lex 6

您不能以这种方式构造循环引用.它不起作用的原因是因为def计算作为绑定传递的表单,并且评估解析为Var的符号获取该Var的当前绑定.换句话说,有什么获得放入地图foo不是引用到变量bar,它的价值bar.bar在事实之后重新定义不会影响fooClojure的不可变性.

前向声明通常用于允许函数之间的循环依赖.以下是有效的,因为在实际调用函数之前不会对函数体进行求值; 当定义函数时,它确实是对已编译的Var的引用.

 (declare bar)

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

 (defn bar
   ([x] (foo x 3))
   ([x y] (+ x y)))
Run Code Online (Sandbox Code Playgroud)