Executing code in another Clojure namespace, why is eval required?

Viewed 837

I'm looking at the implementation for the old contrib macro with-ns:

(defmacro with-ns
  "Evaluates body in another namespace. ns is either a namespace
   object or a symbol. This makes it possible to define functions in
   namespaces other than the current one."
  [ns & body]
  `(binding [*ns* (the-ns ~ns)]
     ~@(map (fn [form] `(eval '~form)) body)))

What I don't understand is the need to eval the body. In other words, why doesn't this work in the case where I want to access elements in the target namespace within the body w/o the eval (example below).

user=> (defmacro wns [ns & body] `(binding [*ns* (the-ns ~ns)] ~@body))
#'user/wns
user=> (create-ns 'boofar)
#<Namespace boofar>
user=> (in-ns 'boofar)
#<Namespace boofar>
boofar=> (clojure.core/refer-clojure)
nil
boofar=> (defn xx [a b] (str a b))
#'boofar/xx
boofar=> (xx 5 6)
"56"
boofar=> (in-ns 'user)
#<Namespace user>
user=> (with-ns 'boofar (println *ns*) (xx 5 6))
#<Namespace boofar>
"56"
user=> (wns 'boofar (println *ns*) (xx 5 6))

CompilerException java.lang.RuntimeException: 
    Unable to resolve symbol: xx in this context, 
    compiling:(blardy/blardy/form-init3758076021118964250.clj:1:29)
user=> (wns 'boofar (println *ns*))
#<Namespace boofar>
nil    

I'm OK with this. I really don't mind, but I'd like to understand what's going on here. xx clearly exists in the boofar namespace, and pushing the ns binding should put me there, yet I can't call it directly, however I can call something in clojure.core which has been referred. In case you're wondering, I tried re-writing the macro to use in-ns with the corresponding try/finally and the result is the same.

Thanks!

edit

Added example of the macro using in-ns. Results are the same as without.

user=> (defmacro wins [ns & body] 
  `(let [orig-ns# (ns-name *ns*)] 
     (try 
       (in-ns ~ns) 
       ~@body 
       (finally (in-ns orig-ns#)))))

The expanded macro...

user=> (pprint (macroexpand-all '(wins 'boofar2 (xx 7 8))))
(let*
 [orig-ns__4137__auto__ (clojure.core/ns-name clojure.core/*ns*)]
 (try
  (clojure.core/in-ns 'boofar2)
  (xx 7 8)
  (finally (clojure.core/in-ns orig-ns__4137__auto__))))

Using it...

user=> (defn xx [a b] (str "user/xx [" a " " b "]"))
user=> (in-ns 'boofar2)
boofar2=> (defn xx [a b] (str "boofar2/xx [" a " " b "]"))
boofar2=> (in-ns 'user)
user=> (wins 'boofar2 (xx 7 8))
"user/xx [7 8]"
1 Answers
Related