Clojure 的加载字符串在 repl 中有效,但在 `lein run` 中无效
Posted
技术标签:
【中文标题】Clojure 的加载字符串在 repl 中有效,但在 `lein run` 中无效【英文标题】:Clojure's load-string works in the repl but not in `lein run` 【发布时间】:2021-11-27 01:03:09 【问题描述】:当我使用 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))
【问题讨论】:
在 Stack Overflow 上,回答你自己的问题完全没问题。但请在新的答案中这样做,而不是在编辑您的问题时这样做。 ***.com/help/self-answer 在 SO 上为您的问题添加您自己的答案非常好 【参考方案1】:哇,我以前从未见过这个功能!
根据文档,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 生成了 var x
,其值为 5
,然后对 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
以空环境开头。
【讨论】:
区别在于*ns*
绑定在repl中,但通过-main
运行时没有。 load-string
对 repl 一无所知,它只是 eval
s 一次形成一个。
感谢 Alan Thompson 的详尽回答。通过@amalloy 的最后评论,我了解了如何解决问题,并为我的问题添加了解决方案。以上是关于Clojure 的加载字符串在 repl 中有效,但在 `lein run` 中无效的主要内容,如果未能解决你的问题,请参考以下文章
无法在spacemacs cider(Clojure)中切换到repl
在 Clojure 中使用 repl 而不是 println
markdown Clojure中Java类的漂亮打印方法。在REPL中有用。
Clojure 的 Emacs/Swank/Paredit 教程