【问题标题】:Run embedded code from a different namespace从不同的命名空间运行嵌入式代码
【发布时间】:2012-12-06 21:22:00
【问题描述】:

我经常想在另一个命名空间中运行一小段 sn-p 代码 - 例如,可能是 DSL 代码的复制/粘贴 sn-p 代码,我希望避免这样做:

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

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

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

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

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

【问题讨论】:

  • 我使用 load-file 来获得这个,这是一个 hack :-/ 所以你不是唯一的,但它仍然可能很疯狂。
  • @Arthur - 嗯,看看它是如何工作的,但 sn-p 通常不在一个自包含的文件中,当我们不需要时,这似乎是对文件系统的滥用。很高兴在 Conj BTW 见到你!
  • 这是有限制的,因为 DSL 使用(在我的情况下,network+vm 定义需要每个文件一个,所以这不是一个好的答案。见到你也很酷:)

标签: clojure namespaces


【解决方案1】:

试试这个:

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

【讨论】:

    【解决方案2】:

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

    注意:在某些情况下它可能会中断,因为我只是通过一个简单的示例进行了尝试

    ;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
    

    【讨论】:

    • 我认为这个解决方案的一个很大的缺点(至少在目前的形式中)是它会取代任何嵌套的'let'块中绑定的符号。但我仍然 +1。
    【解决方案3】:

    我最终在旧的 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)))
    

    【讨论】:

    • 这不是解决方案,因为临时命名空间“ns”中没有来自当前 ns(您的示例中为“baz”)的符号。或者您的问题只是关于临时完整命名空间切换而无法访问当前 ns 的符号?
    • 您可以调整宏以引用其他命名空间。
    猜你喜欢
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多