3

I've got the following in a clojure file:

(ns helloworld
  (:gen-class
    :main -main))

(defn hello-world-fn []
  (println "Hello World"))

(defn -main [& args]
  (eval (read-string "(hello-world-fn)")))

and I'm running it with

lein run helloworld

and I'm getting the following error:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol:
 helloworld in this context, compiling:(helloworld.clj:12)

I have a feeling I need to do something with ns-resolve or resolve but I haven't had any success. I've tried the following in the main function:

(let [call-string  (read-string "(hello-world-fn)")
      func (resolve  (symbol (first call-string)))
      args (rest call-string)]
   (apply func args))

Without success.

Can someone (a) point me in the right direction; and (b) explain precisely what is going on in the Clojure reader when this occurs?

2 Answers 2

6

Try to see what the actual namespace is inside your -main.

(defn -main [& args]
  (prn *ns*)
  (eval (read-string "(hello-world-fn)")))

It outputs #<Namespace user> before bombing out with the exception. This hints that execution of programs with lein run starts out in the user namespace, which obviously does not contain the mapping for your hello-world-fn symbol. You'll need to explicitly qualify it.

(defn -main [& args]
  (eval (read-string "(helloworld/hello-world-fn)")))
Sign up to request clarification or add additional context in comments.

3 Comments

When calling (hello-world-fn) directly, it works. Why does it behave different than eval?
@YehonathanSharvit Because the symbol hello-world-fn will be resolved at compile-time in that case, so the namespace declaration near the top the source file is relevant. "(hello-world-fn)", on the other hand, is just a string. The compiler does not look into strings to namespace-qualify them, because it would be wrong to do so; therefore, eval, which runs at run-time, needs to resolve name references by looking at the *ns* variable when executed.
Really like the debugging approach and solving it directly. I thought the other one was more out of the box and more elegant. Appreciate @MatthiasBenkard explaining the compile phases.
3

You can solve your challenge, in a very elegant way, using macros. In fact, you can write a macro that mimics eval.

(defmacro my-eval [s] `~(read-string s))
(my-eval "(hello-world-fn)")); "Hello World"

It works better that eval because the symbol resolution of s occurs in the context that calls my-eval. Thanks to @Matthias Benkard for the clarifications.

You can read about macros and their syntax in http://clojure.org/reader

5 Comments

Why does this solve the namespace issue automatically when ns-resolve doesn't?
@hawkeye Because resolve works at run-time and will resolve symbols within the current namespace, which is whatever the *ns* variable is pointing to at the time, whereas macro-expanded code within the context of a file is processed by the file compiler, which resolves symbols according to the namespace declarations within the file.
Thanks - so if *ns* is already pointing to helloworld in the example about - why doesn't it resolve the function?
@hawkeye You need to distinguish strictly between run-time and compile-time. At compile-time, the namespace something is being compiled within is known, but after compilation, the code is fully resolved and namespace-qualified, and thus independent of the run-time current namespace. Code passed to eval is resolved based on the *ns* variable at that point in time, that is, at run-time, not at compile-time. The *ns* variable doesn't magically change when you call a function compiled within another namespace (that would be pretty weird behavior, IMHO). You have to bind *ns* manually.
Much more elegant and flexible. Really like the explanations from @MatthiasBenkard as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.