clojure 动态绑定、读取字符串和 eval 无法解析符号
clojure dynamic binding, read-string and eval Unable to resolve symbol
(declare ^:dynamic symbol-table)
(defn answer []
(prn "blah")
(binding [symbol-table {:answer 42}]
(-> "[:h1 (:answer symbol-table)]" read-string eval)))
上面的代码 运行 在 repl 执行时符合预期。它 returns
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)
为什么会发生这种情况以及如何解决?
一些实验表明,由于命名空间的原因,它找不到符号。
例如,我没有从读取字符串中获取表达式,而是输入了文字
(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 不会
当您 运行 eval 时,表单中的不合格符号在 运行time 的当前命名空间中解析(不是函数已定义)。
要解决这个问题,您可以创建一个版本的 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)))
(declare ^:dynamic symbol-table)
(defn answer []
(prn "blah")
(binding [symbol-table {:answer 42}]
(-> "[:h1 (:answer symbol-table)]" read-string eval)))
上面的代码 运行 在 repl 执行时符合预期。它 returns
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)
为什么会发生这种情况以及如何解决?
一些实验表明,由于命名空间的原因,它找不到符号。 例如,我没有从读取字符串中获取表达式,而是输入了文字
(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 不会
当您 运行 eval 时,表单中的不合格符号在 运行time 的当前命名空间中解析(不是函数已定义)。
要解决这个问题,您可以创建一个版本的 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)))