【问题标题】:How can I define a variable in another namespace within a function/macro in Clojure?如何在 Clojure 的函数/宏内的另一个命名空间中定义变量?
【发布时间】:2013-08-09 03:17:20
【问题描述】:

我正在 Clojure 中尝试使用 ns,这是我尝试的方法:

user=> (in-ns 'some-ns)
#<Namespace some-ns>
some-ns=> (def aa 100)
#'some-ns/aa
some-ns=> (in-ns 'user)
#<Namespace user>
user=> (= some-ns/aa 100)
true
user=> (= user/aa 100)
CompilerException java.lang.RuntimeException: No such var: user/aa, compiling:(NO_SOURCE_PATH:5:1) ;this works as expected
user=> (defn function [] (in-ns 'some-other-ns) (def cc 100) (in-ns 'user))
#'user/function
user=> (function)
#<Namespace user>
user=> (= some-other-ns/cc 100)
CompilerException java.lang.RuntimeException: No such var: some-other-ns/cc, compiling:(NO_SOURCE_PATH:8:1)
user=> (= user/cc 100)
true

我很困惑,为什么它在功能上不起作用?另外,我尝试了以下操作:

user=> (binding [*ns* (create-ns 'some-a-ns)] (def dd 100))
#'user/dd
user=> (= some-a-ns/dd 100)
CompilerException java.lang.RuntimeException: No such var: some-a-ns/dd, compiling:(NO_SOURCE_PATH:11:1) 
user=> (= user/dd 100)
true

根据clojure doc

使用符号名称和当前命名空间 (*ns*) 的值的命名空间创建和实习或定位一个全局变量。

我错过了什么?

PS。我知道我可以使用(intern 'some-ns 'a 100),但我真正想要的是一个通用函数/宏来做类似

(with-ns 'some-ns (def a 100))
(= some/a 100)

【问题讨论】:

  • 我不建议使用它,但这会起作用:(do (in-ns 'some-a-ns) (def dd 100)) - 将dd 定义为some-a-ns/dd

标签: clojure namespaces


【解决方案1】:

intern 是正确的解决方案,您可以在自己的任何函数/宏中使用它。 (函数可以调用intern;宏可以扩展为代码调用intern。)

def 只能直接在顶层使用或嵌套在顶层表单中,一旦顶层表单出现,它将立即执行。所以,let 中的def 可以,函数内部的def 则不行。

def 接收来自编译器的特殊处理,因为 def 表单定义的 Vars 是在 def 编译后实际创建的;但是,当控制流实际到达def 表单时,会安装def 表单中指定的初始绑定。这解释了为什么 binding 示例不起作用 - 重要的是 *ns* 的编译时值,而此 binding 表单引入的绑定将在运行时生效。

最后,如果你绝对坚持在运行时使用def 表单来创建Vars,那么可以使用eval

(binding [*ns* some-ns]
  (eval '(def foo 1)))

;; some-ns/foo comes into existence with a root binding of 1

注意这里def确实出现在顶层并且会在编译后立即执行。

【讨论】:

  • 我将在此答案中跳过关于 eval 高运行时成本的标准 cmets。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 2011-01-30
  • 2013-01-02
  • 2021-01-01
  • 1970-01-01
相关资源
最近更新 更多