Динамическое связывание clojure, read-string и eval Невозможно разрешить символ
(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 ответ
Когда вы запускаете 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)))