I often want to run a small snippet of code in another namespace - perhaps a copy/pasted snippet of DSL code for example, and I'd like to avoid having to either:
use
clauses to my current namespace declaration. This makes the ns declaration messy, adds extra maintenance work and sometimes risks name clashes.require
clauses and be forced to add a namespace qualifier or alias to everything. Now my DSL code is much messier.Ideally I'd prefer to be able to do something like:
(with-ns my.namespace
(foo bar baz))
Where foo
, bar
might be symbols within my.namespace
, but baz
is a symbol in the current (enclosing) namespace. So the code is running in something like a "local" namespace that "uses" my-namespace within its scope but otherwise doesn't affect the surrounding namespace.
Is there a standard/better way to do this? Or is this a crazy thing to want to do?
Try this one:
(defmacro with-ns [[namespace symbols] & body]
`(do (use '[~namespace :only ~symbols])
(let [result# (do ~@body)]
(doseq [sym# (map #(:name (meta (val %)))
(filter #(= (name '~namespace)
(str (:ns (meta (val %)))))
(ns-refers *ns*)))]
(ns-unmap *ns* sym#))
result#)))
(with-ns [clojure.string [split upper-case]]
(split (upper-case "it works!") #" "))
-> ["IT" "WORKS!"]
After work it removes used symbols from current ns.
This can be achieved using a macro as shown below.
NOTE: It may break in certain cases as I just tried it with a simple example
;Some other ns
(ns hello)
(def h1 10) ;in hello
(def h2 11) ;in hello
;main ns in which executing code
(ns user)
(defmacro with-ns [target-ns body]
(clojure.walk/postwalk
(fn [val]
(if (symbol? val)
(if (resolve (symbol (str target-ns "/" val)))
(symbol (str target-ns "/" val))
val) val)) body))
(def u1 100) ;in user
(with-ns hello (do (+ h1 u1))) ;110
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