5

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:

  • Add a bunch of use clauses to my current namespace declaration. This makes the ns declaration messy, adds extra maintenance work and sometimes risks name clashes.
  • Add 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?

3
  • I use load-file to sort of get this, it's a hack :-/ So you're not the only one, it could still be crazy though. Commented Dec 6, 2012 at 21:30
  • @Arthur - hmmm see how that could work but often the snippet isn't in a self contained file, also it seems like abuse of the filesystem when we dont need to. Good meeting you at the Conj BTW! Commented Dec 6, 2012 at 21:34
  • It is limiting because the DSL uses (in my case network+vm definitions need to be one per file so it's not a good answer. It was cool to meet you as well :) Commented Dec 6, 2012 at 21:50

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

Comments

3

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

1 Comment

I think a big drawback with this solution (at least in its current form) is that it would supersede symbols bound in any nested 'let' blocks. But still +1 from me.
0

I eventually found a macro in the old Clojure contrib that does part of this quite neatly:

(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)))

2 Comments

It's not the solution because there is no symbols from current ns ("baz" in your example) in temporary namespace "ns". Or your question was just about temporary full namespace switching without access to symbols from current ns?
You could adapt the macro to refer the other namespace.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.