clojure动态绑定,read-string和eval无法解析符号

时间:2017-11-08 10:28:05

标签: multithreading clojure eval dynamic-binding

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

上面的代码在repl执行时按预期运行。它返回

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

然而,当它在由http-kit产生的线程中执行时,我得到一个无法解析的符号

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)

在repl中模拟这个,产生一个线程来运行应答函数

  (.. (Thread. answer) start)

为什么会发生这种情况以及如何解决?

一些实验表明,由于命名空间,它无法找到符号。 例如,我没有从read-string获取表达式,而是输入了一个文字

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

第一个eval使用语法引用,但是当我使用常规引用时它不起作用。语法引用解析命名空间,而常规引用则不解析命名空间。如果read-string返回一个带有命名空间限定符号的表达式,那么它会解决我的问题,但是read-string不会

1 个答案:

答案 0 :(得分:0)

运行eval时,表单中的非限定符号将在运行时的当前命名空间中解析(而不是定义函数的命名空间)。

要解决此问题,您可以创建一个eval版本,其命名空间绑定到您需要的名称:

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

(显然您需要更改my-namespace以反映正确的名称)。然后你改用它:

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