Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 604
Markus Joschko Avatar asked Dec 18 '22 06:12

Markus Joschko


2 Answers

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)
like image 159
cgrand Avatar answered May 30 '23 11:05

cgrand


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.

like image 44
alanlcode Avatar answered May 30 '23 11:05

alanlcode