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]"
Setting ns with bind doesn't quite work that way, which is why the doc's for namespaces include this warning:
"The current namespace, *ns* can and should be set only with a call to in-ns or the ns macro, both of which create the namespace if it doesn't exist."
In your example the symbol xx is being resolved in the namespace user instead of the namespace that *ns*is bound to:
user> (defn xx [a b] "I'm the xx from ns user")
#'user/xx
user> (wns 'boofar (println *ns*) (xx 5 6))
#<Namespace boofar>
"I'm the xx from ns user"
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