I try to write a macro in clojure that sets up a namespace and automatically adds a few methods to it. My macro wasn't working and I tracked it down to a do statement. It is not possible to declare a new namespace in a do and immediately afterwards declare a method in that namespace. Why?
This does not work:
(ns xyz)
(do
(ns abc)
(prn *ns*)
(defn tst[] (prn "test" *ns*)))
(prn "after" *ns*)
(tst)
This works (namespace declaration before the do):
(ns xyz)
(ns abc)
(do
(prn *ns*)
(defn tst[] (prn "test" *ns*)))
(prn "after" *ns*)
(tst)
Thanks for reading, Markus
Actually your code happens to work with Clojure >1.0 but don't rely on that! Each top level form is compiled and then executed:
(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
In clojure > 1.0, a do a the top level is "unrolled" so your code is now equivalent to:
(ns xyz)
; (do
(ns abc)
(prn *ns*)
(defn tst[] (prn "test" *ns*));)
(prn "after" *ns*)
(tst)
Your code does not do what you probably think it does. The function tst
will print the value of *ns*, a var, at the time the function is run, not when it is defined.
user> (ns foobar)
nil
foobar> (abc/tst)
"test" #<Namespace foobar>
nil
foobar> (ns zelsbot)
nil
zelsbot> (abc/tst)
"test" #<Namespace zelsbot>
nil
What you are trying to do has already been nicely provided by clojure.contrib.with-ns
:
(ns xyz
(:use clojure.contrib.with-ns))
(with-ns (create-ns 'abc)
(defn tst [] (print "defined in namespace abc")))
It evaluates its body in the namespace you provide as its first argument, thus allowing you to add functions to a namespace other than the current one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With