为什么我不能使用Clojure声明命名空间和方法

时间:2009-08-25 19:04:46

标签: clojure namespaces

我尝试在clojure中编写一个宏来设置命名空间并自动添加一些方法。我的宏没有工作,我将其跟踪到一个do语句。在do中声明一个新的命名空间是不可能的,然后立即在该命名空间中声明一个方法。为什么呢?

这不起作用:

(ns xyz)
(do
  (ns abc)
  (prn *ns*)
  (defn tst[] (prn "test" *ns*)))

(prn "after" *ns*)
(tst)

这有效(do之前的名称空间声明):

(ns xyz)
(ns abc)
(do
  (prn *ns*)
  (defn tst[] (prn "test" *ns*)))

(prn "after" *ns*)
(tst)

感谢阅读,  马库斯

3 个答案:

答案 0 :(得分:7)

实际上你的代码恰好适用于Clojure> 1.0但不依赖于它! 每个顶级表单都会被编译然后执行:

(ns xyz) ; compiled with current ns / exec sets the current ns to xyz
(do ; the whole form is compiled inthe current ns (xyz) so it defines xyz/tst
  (ns abc)
  (prn *ns*)
  (defn tst[] (prn "test" *ns*)))
; the form above is executed: it sets the new current ns 
; and the value for xyz/tst

(prn "after" *ns*) ; we are in abc
(tst) ; abc/tst doesn't exists

在clojure> 1.0,顶级是“展开”,所以你的代码现在相当于:

(ns xyz)
; (do
(ns abc)
(prn *ns*)
(defn tst[] (prn "test" *ns*));)

(prn "after" *ns*)
(tst)

答案 1 :(得分:4)

您的代码没有按照您的想法执行。函数tst 将在函数运行时打印* ns *,var的值,而不是在定义时打印。

user> (ns foobar)
nil
foobar> (abc/tst)
"test" #<Namespace foobar>
nil
foobar> (ns zelsbot)
nil
zelsbot> (abc/tst)
"test" #<Namespace zelsbot>
nil

clojure.contrib.with-ns已经很好地提供了您要做的事情:

(ns xyz
  (:use clojure.contrib.with-ns))

(with-ns (create-ns 'abc)
  (defn tst [] (print "defined in namespace abc")))

它在您提供的命名空间中评估其主体作为其第一个参数,从而允许您将函数添加到当前命名空间以外的命名空间。

答案 2 :(得分:1)

它是“宏扩展时间”时间与“运行时间”的问题。你想要代码吗(在程序编译时或用户运行程序时会发生吗?) (ns ...)行由阅读器扩展,然后由编译器用来决定在何处放置生成的编译代码 如果我理解正确,ns设置var的顶级绑定,告诉编译器的其余部分它正在构建代码的名称空间。虽然实验尝试考虑(prn ...)表达式应该进入的名称空间。