我经常想在另一个命名空间中运行一小段代码 - 例如,可能是DSL代码的复制/粘贴代码段,我想避免使用其中一个:
use
子句添加到我当前的命名空间声明中。这使得ns声明变得混乱,增加了额外的维护工作,有时会冒名字冲突。require
子句,并强制为所有内容添加命名空间限定符或别名。现在我的DSL代码更加混乱。理想情况下,我更愿意做类似的事情:
(with-ns my.namespace
(foo bar baz))
foo
,bar
可能是my.namespace
中的符号,但baz
是当前(封闭式)命名空间中的符号。因此,代码运行的类似于“本地”命名空间,在其范围内“使用”my-namespace,但不会影响周围的命名空间。
有没有标准/更好的方法来做到这一点?或者这是一件疯狂的事情?
答案 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)))