mikera
mikera

Reputation: 106351

Run embedded code from a different namespace

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:

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?

Upvotes: 5

Views: 206

Answers (3)

mikera
mikera

Reputation: 106351

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

Upvotes: 0

mobyte
mobyte

Reputation: 3752

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.

Upvotes: 4

Ankur
Ankur

Reputation: 33637

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

Upvotes: 3

Related Questions