我可以将Clojure表单从一个包转换为另一个包吗?

时间:2013-03-08 12:11:04

标签: clojure namespaces lisp symbols

背景

我为Emacs编写了一个hack,它允许我将Clojure表单从编辑器缓冲区发送到REPL缓冲区。它工作正常,除非如果两个缓冲区位于不同的名称空间中,复制的文本通常没有意义,或者更糟糕的是,它可能有意义但与编辑器缓冲区中的含义不同。

我想转换文本,以便在REPL缓冲区中有意义。

Common Lisp中的解决方案

在Common Lisp中,我可以使用以下函数执行此操作:

;; Common Lisp

(defun translate-text-between-packages (text from-package to-package)
  (let* ((*package* from-package)
         (form (read-from-string text))
         (*package* to-package))
    (with-output-to-string (*standard-output*)
                           (pprint form))))

使用示例:

;; Common Lisp

(make-package 'editor-package)
(make-package 'repl-package)

(defvar repl-package::a)

(translate-text-between-packages "(+ repl-package::a b)"
                                 (find-package 'editor-package)
                                 (find-package 'repl-package))
;; => "(+ A EDITOR-PACKAGE::B)"

输入字符串和输出字符串中的包名称限定条件不同 - 正是解决在包之间复制和粘贴文本的问题所需要的。

(顺便说一句,有关于如何在Common Lisp进程中运行翻译代码并在Emacs世界和Common Lisp世界之间移动内容的东西,但我对此很好,我并不特别想进入它在这里。)

Clojure中的非解决方案

这是Clojure的直接翻译:

;; Clojure

(defn translate-text-between-namespaces [text from-ns to-ns]
  (let [*ns* from-ns
        form (read-string text)
        *ns* to-ns]
    (with-out-str
      (clojure.pprint/pprint form))))

使用示例:

;; Clojure

(create-ns 'editor-ns)
(create-ns 'repl-ns)

(translate-text-between-namespaces "(+ repl-ns/a b)"
                                   (find-ns 'editor-ns)
                                   (find-ns 'repl-ns))
;; => "(+ repl-ns/a b)"

所以Clojure中的翻译功能没有做任何事情。这是因为Common Lisp和Clojure中的符号和包/命名空间的工作方式不同。

在Common Lisp中,符号属于一个包,符号包的确定在读取时发生。

在Clojure中,出于好的理由,符号不属于命名空间,并且在评估时确定符号的命名空间。

这可以在Clojure中完成吗?

所以,最后,我的问题:我可以将Clojure代码从一个名称空间转换为另一个名称空间吗?

2 个答案:

答案 0 :(得分:1)

我不了解您的用例,但这里有一种方法可以将符号从一个名称空间转换为另一个名称空间。

(require 'clojure.walk 'clojure.pprint)

(defn ns-trans-form [ns1 ns2 form]
  (clojure.walk/prewalk
    (fn [f] (if ((every-pred symbol? #(= (namespace %) ns1)) f)
                (symbol ns2 (name f))
                f))
    form))

(defn ns-trans-text [ns1 ns2 text]
    (with-out-str
      (->> text 
           read-string 
           (ns-trans-form ns1 ns2) 
           clojure.pprint/pprint)))

(print (ns-trans-text "editor-ns" "repl-ns" "(+ editor-ns/a b)" ))
;=> (+ repl-ns/a b)

因此,editor-ns/a已转换为repl-ns/a

答案 1 :(得分:1)

(回答我自己的问题......)

鉴于从命名空间外部引用命名空间的非公共变量并不容易,没有简单的方法可以做到这一点。

根据http://christophermaier.name/blog/2011/04/30/not-so-private-clojure-functions的想法,也许黑客行为是可能的。这将涉及遍历表单并创建新符号,以解析与原始表单中引用的vars具有相同值的新变量。也许我会在某个时候进一步调查,但不是现在。