Resigned June 2023
Resigned June 2023

Reputation: 4937

How to use a symbol only if it is defined

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

Answers (2)

Sam Estep
Sam Estep

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

Michiel Borkent
Michiel Borkent

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

Related Questions