hawkeye
hawkeye

Reputation: 35762

Clojure - (read-string String calling function

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?

Upvotes: 3

Views: 1351

Answers (2)

viebel
viebel

Reputation: 20710

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

Upvotes: 3

Daniel Janus
Daniel Janus

Reputation: 657

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)")))

Upvotes: 6

Related Questions