如何在Clojure中的函数/宏中的另一个命名空间中定义变量?

时间:2013-08-09 03:17:20

标签: clojure namespaces

我在 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 *)的命名空间创建并实习或定位全局var。

我缺少什么?

PS。我知道我可以使用(intern 'some-ns 'a 100),但我真正想要的是像

那样的通用函数/宏
(with-ns 'some-ns (def a 100))
(= some/a 100)

1 个答案:

答案 0 :(得分:5)

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

def只能直接在顶级使用,或嵌套在顶级表单中,只要顶级表单立即执行,它就会立即执行。因此,def中的let很好,函数内的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确实发生在顶级,并会在编译后立即执行。