5

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

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

理想情况下,我希望能够执行以下操作:

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

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

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

4

3 回答 3

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 中删除使用过的符号。

于 2012-12-07T04:45:58.850 回答
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
于 2012-12-07T04:32:47.597 回答
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)))
于 2012-12-10T05:17:40.580 回答