将 Clojure 命名空间拆分为多个文件
Posted
技术标签:
【中文标题】将 Clojure 命名空间拆分为多个文件【英文标题】:Splitting a Clojure namespace over multiple files 【发布时间】:2011-06-09 02:41:14 【问题描述】:在使用 :gen-class
进行提前编译时,是否可以将 Clojure 命名空间拆分为多个源文件? (:main true)
和 (defn- ...)
如何发挥作用?
【问题讨论】:
【参考方案1】:概述
当然可以,事实上clojure.core
命名空间本身就是这样拆分的,并提供了一个很好的模型,您可以通过查看src/clj/clojure
来遵循它:
core.clj
core_deftype.clj
core_print.clj
core_proxy.clj
..etc..
所有这些文件都参与构建单个 clojure.core
命名空间。
主文件
其中一个是主文件,其命名与命名空间名称相匹配,以便当有人在 :use
或 :require
中提及它时可以找到它。在这种情况下,主文件是clojure/core.clj
,它以ns
形式开始。这是您应该放置 all 您的命名空间配置的地方,无论您的其他文件可能需要它们。这通常也包括:gen-class
,所以类似于:
(ns my.lib.of.excellence
(:use [clojure.java.io :as io :only [reader]])
(:gen-class :main true))
然后在您的主文件中的适当位置(最常见的是最后)使用load
来引入您的帮助文件。在clojure.core
中看起来像这样:
(load "core_proxy")
(load "core_print")
(load "genclass")
(load "core_deftype")
(load "core/protocols")
(load "gvec")
请注意,您不需要当前目录作为前缀,也不需要.clj
后缀。
帮助文件
每个帮助文件都应该首先声明它们正在帮助的命名空间,但应该使用in-ns
函数来这样做。所以对于上面的示例命名空间,帮助文件都将以:
(in-ns 'my.lib.of.excellence)
仅此而已。
gen-class
因为所有这些文件都在构建单个命名空间,所以您定义的每个函数都可以位于任何主文件或辅助文件中。这当然意味着您可以在任何您想要的文件中定义您的 gen-class
函数:
(defn -main [& args]
...)
请注意,Clojure 的正常定义顺序规则仍然适用于所有函数,因此您需要确保在尝试使用之前加载了任何文件定义一个函数 em> 那个函数。
私有变量
您还询问了定义命名空间私有函数的(defn- foo ...)
表单。像这样定义的函数以及其他 :private
变量在定义它们的命名空间中是可见的,因此主文件和所有帮助文件都可以访问在迄今为止加载的任何文件中定义的私有变量。
【讨论】:
非常好,完整的答案!顺便说一句,我的第一次通关Clojure 的乐趣 差不多完成了。好书! 感谢分享这个答案。 2 年后,它仍然被认为是一种好的做法吗? (我知道事情变化很快。)我看到 Clojure 本身仍在使用这种技术。 到目前为止,如果您确定希望多个文件生成一个命名空间,这仍然是最佳实践。然而,这本身现在可能不像以前那么普遍了。另一种方法是在单个文件中定义 ns 的所有公共变量,并将所有辅助变量和函数移动到单独的“实现”命名空间。 impl 中的 var 在技术上是公开的,但 ns 文档字符串表明它们不是文档化 API 的一部分是常见的,应该足够了。 我们知道是否有任何常见的 Clojure 工具在理解多文件命名空间方面存在问题?莱因?开机?苹果酒? nREPL?基比特?伊斯特伍德?三叶草?等等……以上是关于将 Clojure 命名空间拆分为多个文件的主要内容,如果未能解决你的问题,请参考以下文章