我正在开发一个Clojure应用程序,它需要能够从shell命令行读取Clojure代码。该应用程序具有-main
函数,该函数读取在命令行上传递的包含单个Clojure表单的字符串,并将此字符串传递给Clojure函数load-string
,该函数解析字符串并执行代码。这使用lein run
和使用uberjar成功运行。
我发现,如果名称空间位于包含required
的文件顶部的-main
,则命令行传递的代码可以包含完全限定的命名空间名称。例如,如果源文件以
(ns popco.core.popco
(:require [popco.core.reporters :as rpt]))
然后我在命令行上传递的字符串可以通过ticker
引用我的函数popco.core.reporters/ticker
。但是,我无法使用别名rpt
。如果我ticker
引用rpt/ticker
,我会收到例外:java.lang.RuntimeException: No such namespace: rpt
。
我猜这是因为别名仅在编译时可用,并且只有一个编译时间。由于load-file
编译代码字符串的时间是在完成源文件编译之后,rpt
不再可用作别名。
允许我使用命名空间别名的解决方案是在require
内复制文件顶部的-main
(我想在repl中运行时使用)。但是,我可能需要包含几个名称空间,当然,复制代码也是不可取的。
还有其他解决方案吗?一些定义别名的方法,在repl中运行和从命令行运行代码时都可用吗?
(编辑:关于编译时间的分析可能非常正确 - 或者我可能不理解Clojure编译。(其实,我不理解Clojure汇编!)上面两段中提到的解决方案使用require
内原始源代码中的-main
语句,而不仅仅是传递给load-file
的字符串。所以{{1}我想,在编译require
时会编译,这会在与源文件顶部相同的传递过程中进行编译。但是-main
中的某些代码在别名的范围内如果代码被输入到-main
的定义中,则文件的顶部,但如果它是通过-main
引入的,则不是。但是通过{{1}引入的是什么当load-file
完全位于load-file
的源代码中时,属于别名的范围。为什么?)
答案 0 :(得分:1)
alias
函数,该机制需要用于通过:as
密钥创建命名空间别名,改变当前命名空间。也就是说,alias
时的当前命名空间会运行。
这就是嵌入require
定义的-main
以您期望的方式创建别名的原因:它正在改变您的运行时命名空间以包含给定的别名。强调,这不一定会改变popco.core.popco
命名空间!事实上,在调用-main
时,它不可能是运行时的工作命名空间(它将在编译时,因此会像在ns
宏中一样,改变被定义的ns。)
这里的关键是要意识到ns
在运行时并不总是定义ns
函数的-main
,如果你想在运行时改变评估环境,那么你需要切换到您正在变异的命名空间,或者改变您所在的命名空间。