从不同的命名空间运行嵌入式代码

时间:2012-12-06 21:22:01

标签: clojure namespaces

我经常想在另一个命名空间中运行一小段代码 - 例如,可能是DSL代码的复制/粘贴代码段,我想避免使用其中一个:

  • 将一堆use子句添加到我当前的命名空间声明中。这使得ns声明变得混乱,增加了额外的维护工作,有时会冒名字冲突。
  • 添加require子句,并强制为所有内容添加命名空间限定符或别名。现在我的DSL代码更加混乱。

理想情况下,我更愿意做类似的事情:

(with-ns my.namespace
  (foo bar baz))

foobar可能是my.namespace中的符号,但baz是当前(封闭式)命名空间中的符号。因此,代码运行的类似于“本地”命名空间,在其范围内“使用”my-namespace,但不会影响周围的命名空间。

有没有标准/更好的方法来做到这一点?或者这是一件疯狂的事情?

3 个答案:

答案 0 :(得分:4)

试试这个:

(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!"]

下班后,它会从当前的ns中删除使用过的符号。

答案 1 :(得分:3)

这可以使用宏来实现,如下所示。

注意:在某些情况下可能会中断,因为我只是用一个简单的例子来尝试它

;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

答案 2 :(得分:0)

我最终在旧的Clojure contrib中发现了一个宏,它完全可以做到这一点:

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