背景
我为 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 代码从一个命名空间转换到另一个命名空间吗?