Search code examples
clojureread-eval-print-loopleiningenuberjarclojure-1.3

Clojure symbol evaluation error


So I currently have this code:

(ns contact-form.core
  (:gen-class))

(def foo "Hello World!")

(defn some-func [a-symbol]
  (println (str a-symbol "'s value is: " (eval a-symbol))))

(defn -main [& args]
  (some-func 'foo))

After I do C-c C-k in Emacs, I get the following output:

contact-form.core> (-main)
foo's value is: Hello World!
nil

But when I do lein uberjar and run the resulting jar file, I get an error:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:0)
    at clojure.lang.Compiler.analyze(Compiler.java:6235)
    at clojure.lang.Compiler.analyze(Compiler.java:6177)
    at clojure.lang.Compiler.eval(Compiler.java:6469)
    at clojure.lang.Compiler.eval(Compiler.java:6431)
    at clojure.core$eval.invoke(core.clj:2795)
    at contact_form.core$some_func.invoke(core.clj:7)
    at contact_form.core$_main.doInvoke(core.clj:10)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at clojure.lang.AFn.applyToHelper(AFn.java:159)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at contact_form.core.main(Unknown Source)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: foo in this context
    at clojure.lang.Util.runtimeException(Util.java:156)
    at clojure.lang.Compiler.resolveIn(Compiler.java:6720)
    at clojure.lang.Compiler.resolve(Compiler.java:6664)
    at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6625)
    at clojure.lang.Compiler.analyze(Compiler.java:6198)
    ... 10 more

So I have two questions:

  1. Why does the uberjar not function exactly the same as the REPL?
  2. What can I do to fix this problem?

Solution

  • 1. Why does uberjar function differently to the REPL?

    A cause of the error "NO_SOURCE_PATH" is that you are not presently in the namespace that defined 'foo.

    To illustrate: if I evaluate your code in my REPL and execute it, it places me in the contact-form.core namespace as you would expect because (ns contact-form.core) is evaluated by the REPL, but if I switch to the user namespace and call -main I can produce the same error:

    contact-form.core=> (-main)
    foo's value is: Hello World!
    nil
    contact-form.core=> (ns user)
    nil
    user=> (contact-form.core/-main)
    CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:120) 
    user=> 
    

    So, by specifying your entry point to main for standalone uberjar execution (outside the REPL), it is equivalent to calling (contact-form.core/-main) from the default namespace in your jar which is clojure.core, because (ns contact-form.core) has not been evaluated. Result: main can be executed with a fully qualified (namespaced) path to the function, but none of the symbols from contact-form.core are available in the current default namespace.

    2. The fix

    The solution would be to explicitly switch to your namespace first.:

    (defn -main [& args]
      (use 'contact-form.core)
      (some-func 'foo))