【问题标题】:clojure dynamic binding, read-string and eval Unable to resolve symbolclojure 动态绑定、读取字符串和 eval 无法解析符号
【发布时间】:2017-11-08 10:28:04
【问题描述】:
  (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 处模拟这一点,请生成一个线程来运行 answer 函数

  (.. (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 没有

【问题讨论】:

    标签: multithreading clojure eval dynamic-binding


    【解决方案1】:

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

    要解决这个问题,您可以创建一个 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)))
    

    【讨论】:

    • 这可行,但如果我不知道命名空间怎么办? eval 应该获取一个环境映射,它可以从中解析符号。
    • 既然定义了 var,就必须知道它是在哪里定义的,不是吗?
    • 是的,但上下文是我将打嗝存储在一个文件中,该文件包含应该使用符号表来解析的表达式和值。调用 eval 的人不需要指定其符号表所在的命名空间。它使 api 变得丑陋。例如,我想调用 (eval-hiccup (read-string a-file) {:answer 42, age: 10000000})。将命名空间作为符号表中的键传递应该可以工作,但它会使 api hacky
    猜你喜欢
    • 2019-09-05
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 2015-12-25
    相关资源
    最近更新 更多