1
votes
  (declare ^:dynamic symbol-table)
  (defn answer []
    (prn "blah")
    (binding [symbol-table {:answer 42}]
      (-> "[:h1 (:answer symbol-table)]" read-string eval)))

The above code runs as expected when executed at the repl. it returns

cpress.hsp> (answer)
"blah"                                                                                                                                                                                      
[:h1 42]

However, when it is executed in a thread spawn by http-kit, I get an unable to resolve symbol

Exception in thread "Thread-43" 
java.lang.RuntimeException: Unable to resolve symbol: symbol-table in this context, compiling:(NO_SOURCE_PATH:0:0)
        at clojure.lang.Compiler.analyze(Compiler.java:6792)
        at clojure.lang.Compiler.analyze(Compiler.java:6729)
        at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3874)
        at clojure.lang.Compiler.analyzeSeq(Compiler.java:7005)
        at clojure.lang.Compiler.analyze(Compiler.java:6773)

to simulate this at the repl spawn a thread to run the answer function

  (.. (Thread. answer) start)

Why does this happen and how to fix it?

Some experimentation shows that it can't find the symbol due to namespace. for example, instead of getting the expression from read-string, i put in a literal

  (defn answer2 []
    (binding [symbol-table {:answer 42}]
      (prn (eval `[:h1 (:answer symbol-table)])) ;;works                                                                                                                                    
      ;;(eval '[:h1 (:answer symbol-table)]) ;; does not works                                                                                                                              
      ))

the first eval uses syntax quoting which works, but when i use regular quoting it doesn't work. syntax quoting resolves namespace while regular quoting does not. if read-string returned an expression with namespace qualified symbols then it would solve my problem I think, but read-string does not

1

1 Answers

3
votes

When you run eval, unqualified symbols in the form are resolved in the current namespace at runtime (not that of the namespace where the function is defined).

To solve this, you can create a version of eval with the namespace bound to the one you need:

(defn local-eval [x]
  (binding [*ns* (find-ns 'my-namespace)]
    (eval x)))

(Obviously you need to change my-namespace to reflect the right name). Then you use that instead:

(defn answer []
  (binding [symbol-table {:answer 42}]
    (-> "[:h1 (:answer symbol-table)]" read-string local-eval)))