Reputation: 4937
I would like to execute some Clojure code that depends upon a certain var, but only if that var is defined. As a simplified example, the body of the if
form should only be executed if sym
is defined:
(if (resolve 'sym) (println sym))
Unfortunately, this doesn't work. If sym
is not defined, the compiler still tries to resolve it and throws:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: sym in this context
From reading Rich Hickley's comment here, I gathered that this behavior is due to Clojure's use of (mostly) single-pass compilation. However, as much sense as that makes, it obviously results in undesirable behavior in this case.
I can get around the problem by forcing the symbol resolution to happen at runtime:
(if (resolve 'sym) (println (deref (resolve 'sym))))
But this is an undesirable hack. Is there a better way, or is it not possible with Clojure's read-eval model?
(Why do I need to do this? I have multiple composable profiles defined in my profiles.clj
. One of them is for vinyasa, which allows me to inject various functions into the conveniently accessible .
namespace. Others load various other utility libraries. In the profiles for those other utility libraries, I want to use vinyasa to inject the most important functions, but only if vinyasa is loaded. I am currently using a variation of the hack above.)
Upvotes: 0
Views: 75
Reputation: 13324
The approach recommended by @Michiel with when-let
is the best way to solve this problem. Importantly, you can make the conditionality almost completely transparent by using let
's ability to shadow an existing binding:
(when-let [foo (resolve 'foo)]
(foo))
;;=> nil
(defn foo []
:bar)
(foo)
;;=> :bar
(when-let [foo (resolve 'foo)]
(foo))
;;=> :bar
Upvotes: 1
Reputation: 34840
(defn get-when-var-defined [sym]
(when-let [var-for-sym (resolve sym)]
@var-for-sym))
(get-when-var-defined 'foo) ;;=> nil
(def foo 1)
(get-when-var-defined 'foo) ;;=> 1
Upvotes: 1