Reputation: 1111
In a namespace, I am defining two vars (amongst others) which are maps:
(declare bar)
(def foo {:is-related-to bar})
(def bar {:is-related-to foo})
Because bar
is not existing when I define foo
, I am forward-declaring it using (declare bar)
.
There is no issues so far, everything is working as expected in the REPL.
The only thing we notice is that when I check foo
in the REPL, I see that bar
is unbound, which I think is to be expected with the usage of declare
:
#<Unbound Unbound: #'user/bar>
The problem arises when I try to compile the software with lein jar
or lein ring war
(since it is a Ring application). The error I am getting from the compiler is:
Exception in thread "main" java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: Unbound: #'user/bar, compiling...
I think this is to be expected as well since I don't think the compiler can handle unbound vars.
In any case, if all these behaviors are to be expected, why are people using forward-declaration if it can't be compiled? I am probably missing something here.
Upvotes: 2
Views: 134
Reputation: 818
If you want to refer to the Symbol bar you should use #'bar. When you use bar you have the value of bar witch is not yet defined. If you use #'bar you refer to the bar symbol witch you can evaluate when you want and will be properly defined...
(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
Upvotes: 2
Reputation: 13941
You cannot construct circular references in this manner. The reason it doesn't work is because def
evaluates the form that is passed as the binding, and evaluating a symbol that resolves to a Var gets the current binding for that Var. In other words, what get put into the map foo
is not a reference to the var bar
, it's the value of bar
. Redefining bar
after the fact doesn't affect the value of foo
- that's the point of immutability in Clojure.
Forward-declarations are typically used to allow circular dependencies between functions. The following works, because the the body of the function is not evaluated until the function is actually invoked; when the function is defined, it is indeed a reference to the Var that gets compiled.
(declare bar)
(defn foo [x y]
(bar x (* 2 y)))
(defn bar
([x] (foo x 3))
([x y] (+ x y)))
Upvotes: 6