7

I get some weird behaviour when checking if a symbol can be resolved.

user=> ok
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ok in this context, compiling:(NO_SOURCE_PATH:0)
user=> (resolve 'ok)
nil
user=> (if (resolve 'ok) "bla" (def ok 'ok))
"bla"
user=> ok
#<Unbound Unbound: #'user/ok>
user=> (def ok 'ok)
#'user/ok
user=> ok
ok

Can anyone tell me where this might come from? Is this behaviour intended?

Dominik G
  • 1,459
  • 3
  • 17
  • 37
  • What version of Clojure are you using? I don't see this on 1.2.1 – okonomichiyaki Jan 14 '12 at 18:11
  • 1
    @spacemanaki: Interesting, I just reproduced this in both 1.2.1 and 1.3 Something strange is definitely happening. – Goran Jovic Jan 14 '12 at 19:24
  • My mistake, second reference to `ok` resulted in an exception instead of returning an `#` object, and I didn't pay attention. For me, on 1.2.1 `(resolve 'ok)` inside the `if` does result in `ok` being declared. – okonomichiyaki Jan 14 '12 at 20:42
  • You'll find [this answer](http://stackoverflow.com/a/4908367/506721) to another question useful. His function `bounded?` seems to do the solve your problem. I replaced `resolve` with it in your example and it worked. As for this behavior, I hope we'll get an answer. – Goran Jovic Jan 14 '12 at 21:21

2 Answers2

4

(def ok "whatever") creates a variable named ok at compile time. The compiler scans the whole form to compile it, discovers that you will be defining a var named ok, and creates it for you (with no binding), before your form is actually executed. When the def form is actually executed, the runtime value of the expression will be assigned to the var user/ok. In your example, this never happens, because the var has already been created, and the if branch goes the other way.

Using bound? as a substitute is a terrible idea, as it tests something quite different: whether the named var (which must exist) has a binding, either permanent or thread-local.

amalloy
  • 89,153
  • 8
  • 140
  • 205
  • sounds logical. But then why does `(if (resolve 'x) x (def x 'x))` yield `CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH:1)`? Or does he try to evaluate x before he creates the variable? – Dominik G Jan 15 '12 at 08:10
1

Since I only use it inside a macro I now use it as follows

(defmacro bla [x]
    (if (resolve x) x `(def ~x '~x)))

And now it works since def is inside the quoted form and evaluated after resolve.

Dominik G
  • 1,459
  • 3
  • 17
  • 37