views:

138

answers:

3

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

+1  A: 

Its a problem of "macro expansion time" time vs. "runtime". Do you want the code in the (do to happen when the program is compiled or when the program is run by the user?
the (ns ...) lines are expanded by the reader and then used by the compiler to decide where to put the resulting compiled code.
If i understand correctly ns sets the top level binding of the var that tells the rest of the compiler what name space it is building code for. As a though experiment try to think about what name space the (prn ...) expression should go in.

Arthur Ulfeldt
+3  A: 

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.

alanlcode
That was meant as a runtime test. Anyhow your with-ns tip is very helpful as I can use it to get the desired functionality.
Markus Joschko
+5  A: 

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)
cgrand
Ah thanks. That explains it. I'll keep this in mind to simply the macro once a stable clojure version > 1.0 is released.
Markus Joschko