在多个文件中拆分Clojure名称空间


91

使用进行提前编译时,是否可以将Clojure命名空间拆分为多个源文件:gen-class?如何(:main true)(defn- ...)发挥作用的?

Answers:


137

总览

当然可以,实际上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表格开头。无论您有哪些其他文件需要它们,都应在此处放置所有名称空间配置。通常还包括: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的常规定义顺序规则仍然适用于所有功能,因此在尝试使用该功能之前,需要确保已加载定义该功能的任何文件。

私人变量

您还询问了(defn- foo ...)定义命名空间专用功能的表单。:private可以在定义它们的名称空间中看到这样定义的函数以及其他变量,因此主文件和所有帮助程序文件都可以访问到目前为止加载的任何文件中定义的私有变量。


3
非常好,完整的答案!顺便说一句,我在《 Clojure的喜悦》的第一次通行证中快要完成了。好书!
拉尔夫

感谢您分享此答案。两年后,仍然认为它是一种好习惯吗?(我知道事情变化很快。)我看到Clojure本身仍然使用这种技术。
David J.

9
到目前为止,如果您确定要多个文件生成一个名称空间,这仍然是最佳实践。但是,这种情况现在可能不那么普遍了。一种替代方法是在单个文件中定义ns的所有公共变量,并将所有辅助变量和函数移至单独的“实现”名称空间。从技术上讲,impl中的vars是公开的,但是一个ns docstring表示它们不属于已记录的API是很常见的,应该足够了。
Chouser 2013年

1
我们是否知道任何通用的Clojure工具在理解多文件名称空间时是否存在问题?莱恩?开机吗?苹果酒?nREPL?基比特 伊斯特伍德?幸运草?等等...
Didier A.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.