Why can't I declare a namespace and method in do using Clojure

I am trying to write a macro in clojure that sets up a namespace and automatically adds several methods to it. My macro did not work, and I traced it to the do statement. It is not possible to declare a new namespace in do and immediately 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 do):

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

Thanks for reading, Marcus.

+4
source share
3 answers

Your code actually works with Clojure> 1.0, but don't rely on it! 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, the top level is "deployed", so your code is now equivalent:

 (ns xyz) ; (do (ns abc) (prn *ns*) (defn tst[] (prn "test" *ns*));) (prn "after" *ns*) (tst) 
+7
source

Your code does not do what you probably think. The tst function will print the value * ns *, var, during the execution of the function, and 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 was already well presented 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 that you provide as the first argument, which allows you to add functions to the namespace other than the current one.

+4
source

This is a "macro expansion time" time issue compared to a "runtime". Do you want the code in (occurs when the program is compiled or when the program is started by the user?
strings (ns ...) 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 var, which tells the rest of the compiler what namespace the build code is used for. As an experiment, try to think about what namespace the expression should have (prn ...).

+1
source

All Articles