Clojure 的加载字符串在 repl 中有效,但在 `lein 运行` 中无效
Clojure's load-string works in the repl but not in `lein run`
当我使用 lein repl
启动 repl 时,我可以 运行 函数 greet
并且它按预期工作。
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."]
(println (load-string "x"))))
(defn -main [& args]
(greet))
但是如果 运行 通过 lein run
的代码会失败
java.lang.RuntimeException: Unable to resolve symbol: x in this context.
我错过了什么?
是否在编译过程中删除了 var x
,尽管已声明,因为它从未在字符串之外使用过?
编辑:
解决方案
@amalloy 的评论帮助我理解我需要绑定 *ns*
以便在预期的命名空间中加载字符串,而不是新的空命名空间。
这按预期工作:
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."
*ns* (find-ns 'var-test.core)]
(println (load-string "x"))))
(defn -main [& args]
(greet))
哇,我以前从没见过这个功能!
根据文档,load-string
旨在从输入字符串中一次一个地读取和加载表单。观察这段代码,由 my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.string :as str]))
(dotest
(def y "wilma")
(throws? (eval (quote y)))
(throws? (load-string "y"))
看来 load-string
从一个新的空环境开始,然后在那个新环境中一次读取和评估一个表单。由于您的 x
不在那个新环境中,因此找不到它并且您收到错误消息。
换一种方式试试:
(load-string
(str/quotes->double
"(def ^:dynamic x)
(binding [x 'fred']
(println :bb (load-string 'x'))) " ))
;=> :bb fred
在这种情况下,我们将所有代码作为文本提供给 load-string
。它首先读取 eval
的 def
,然后是 binding
和嵌套的 load-string
形式。一切都按预期工作,因为工作环境包含 x
.
的 Var
更多代码说明了这一点:
(spy :cc
(load-string
"(def x 5)
x "))
结果
:cc => 5
因此 eval 生成了值为 5
的变量 x
,然后对 x
的引用导致生成值 5
。
令我惊讶的是,部分 load-string
在新的 REPL 中有效:
demo.core=> (def x "fred")
#'demo.core/x
demo.core=> (load-string "x")
"fred"
因此 load-string
必须编码以使用任何预先存在的
REPL 环境作为基础环境。使用 lein run
时,没有可用的 REPL 环境,因此 load-string
从一个空环境开始。
当我使用 lein repl
启动 repl 时,我可以 运行 函数 greet
并且它按预期工作。
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."]
(println (load-string "x"))))
(defn -main [& args]
(greet))
但是如果 运行 通过 lein run
的代码会失败
java.lang.RuntimeException: Unable to resolve symbol: x in this context.
我错过了什么?
是否在编译过程中删除了 var x
,尽管已声明,因为它从未在字符串之外使用过?
编辑:
解决方案
@amalloy 的评论帮助我理解我需要绑定 *ns*
以便在预期的命名空间中加载字符串,而不是新的空命名空间。
这按预期工作:
(ns var-test.core
(:gen-class))
(declare ^:dynamic x)
(defn greet []
(binding [x "Hello World."
*ns* (find-ns 'var-test.core)]
(println (load-string "x"))))
(defn -main [& args]
(greet))
哇,我以前从没见过这个功能!
根据文档,load-string
旨在从输入字符串中一次一个地读取和加载表单。观察这段代码,由 my favorite template project:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [tupelo.string :as str]))
(dotest
(def y "wilma")
(throws? (eval (quote y)))
(throws? (load-string "y"))
看来 load-string
从一个新的空环境开始,然后在那个新环境中一次读取和评估一个表单。由于您的 x
不在那个新环境中,因此找不到它并且您收到错误消息。
换一种方式试试:
(load-string
(str/quotes->double
"(def ^:dynamic x)
(binding [x 'fred']
(println :bb (load-string 'x'))) " ))
;=> :bb fred
在这种情况下,我们将所有代码作为文本提供给 load-string
。它首先读取 eval
的 def
,然后是 binding
和嵌套的 load-string
形式。一切都按预期工作,因为工作环境包含 x
.
更多代码说明了这一点:
(spy :cc
(load-string
"(def x 5)
x "))
结果
:cc => 5
因此 eval 生成了值为 5
的变量 x
,然后对 x
的引用导致生成值 5
。
令我惊讶的是,部分 load-string
在新的 REPL 中有效:
demo.core=> (def x "fred")
#'demo.core/x
demo.core=> (load-string "x")
"fred"
因此 load-string
必须编码以使用任何预先存在的
REPL 环境作为基础环境。使用 lein run
时,没有可用的 REPL 环境,因此 load-string
从一个空环境开始。