我尝试在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)
感谢阅读, 马库斯
答案 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 ...)表达式应该进入的名称空间。