Clojure 从不同的命名空间运行嵌入式代码
我经常希望在另一个名称空间中运行一小段代码—例如,可能是DSL代码的复制/粘贴片段,我希望避免:Clojure 从不同的命名空间运行嵌入式代码,clojure,namespaces,Clojure,Namespaces,我经常希望在另一个名称空间中运行一小段代码—例如,可能是DSL代码的复制/粘贴片段,我希望避免: 在我当前的名称空间声明中添加一组use子句。这会使ns声明变得凌乱,增加额外的维护工作,有时还会出现名称冲突的风险 添加require子句,并强制向所有内容添加名称空间限定符或别名。现在我的DSL代码更混乱了 理想情况下,我更希望能够做到以下几点: (with-ns my.namespace (foo bar baz)) 其中foo,bar可能是my.namespace中的符号,但baz是
- 在我当前的名称空间声明中添加一组
子句。这会使ns声明变得凌乱,增加额外的维护工作,有时还会出现名称冲突的风险use
- 添加
子句,并强制向所有内容添加名称空间限定符或别名。现在我的DSL代码更混乱了require
(with-ns my.namespace
(foo bar baz))
其中foo
,bar
可能是my.namespace
中的符号,但baz
是当前(封闭)命名空间中的符号。因此,代码运行在类似“本地”名称空间的地方,该名称空间在其作用域内“使用”我的名称空间,但不会影响周围的名称空间
有没有标准/更好的方法来实现这一点?或者这是一件疯狂的事情吗?这可以通过使用如下所示的宏来实现 注意:在某些情况下,它可能会断裂,因为我只是用一个简单的示例进行了尝试
;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
试试这个:
(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中删除使用过的符号。我最终在旧的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)))
我使用load file(加载文件)来实现这一点,这是一个黑客行为:-/所以你不是唯一的一个,这可能仍然很疯狂。@Arthur-hmm看到了这一点,但代码片段通常不在一个自包含的文件中,而且在我们不需要的时候,这似乎是对文件系统的滥用。顺便说一句,很高兴在Conj见到你!这是一个限制,因为DSL使用(在我的例子中,网络+虚拟机定义需要每个文件一个,所以这不是一个好的答案。很高兴见到你:)我认为这个解决方案的一个大缺点(至少在它当前的形式下)是它将取代任何嵌套的“let”块中绑定的符号。但仍然是我的+1。这不是解决方案,因为在临时名称空间“ns”中没有来自当前ns(在您的示例中为“baz”)的符号。或者您的问题只是关于临时完整名称空间切换,而不访问当前ns中的符号?您可以调整宏以引用其他名称空间。