Bel*_*lun 7 namespaces clojure
命名空间的目的是什么?
更重要的是,它们是否应该用作java中的对象(具有数据和功能并尝试实现封装的东西)?这个想法是远远不够的?:)
或者它们应该在java中用作包吗?
或者它们应该更普遍地用作模块系统还是什么?
鉴于您使用了Clojure标签,我想您会对Clojure特定的答案感兴趣:
命名空间的目的是什么?
Clojure名称空间,Java包,Haskell/Python /任何模块......在很高的层次上,它们都是相同基本机制的不同名称,其主要目的是防止非平凡代码库中的名称冲突.当然,每种解决方案都有自己的小曲折和怪癖,它们在给定语言的上下文中是有意义的,并且在它之外是没有意义的.这个答案的其余部分将涉及Clojure特有的曲折和怪癖.
Clojure命名空间组合Vars,它们是容纳函数(最常见)的容器,宏函数(编译器用来生成适当形式的宏扩展的函数,通常定义为defmacro;实际上它们只是常规的Clojure函数,尽管它有一些魔力它们在编译器中注册的方式)偶尔会有各种"全局参数"(比如clojure.core/*in*标准输入),Atoms/Refs等.Clojure 1.2中引入的协议工具具有很好的属性,协议由Vars支持,因为各个协议功能; 这是协议提供表达式问题解决方案的关键(但这可能超出了本答案的范围!).
按理说,命名空间应该将Vars组合在一起,这些Vars在某种程度上是相关的.通常,创建命名空间是一种快速而廉价的操作,因此在开发的早期阶段使用单个命名空间是完全正常的(并且通常是常见的),然后当独立的功能块出现时,将它们分解到它们自己的命名空间中,冲洗和重复......只有作为公共API一部分的东西才需要预先在命名空间之间分配(或者更确切地说:在稳定版本之前),因为这样的功能存在于命名空间中,所以 - - 当然是API的一部分.
更重要的是,它们是否应该用作java中的对象(具有数据和功能并尝试实现封装的东西)?这个想法是远远不够的?:)
通常,答案是否定的.如果你将它们作为具有大量静态方法的类,没有实例方法,没有公共构造函数并且通常没有状态(尽管偶尔可能存在某些"类数据成员"),你可能会得到一张距离事实不太远的图片. Vars持有Atoms/Refs); 但可以说,不尝试将Java-ish隐喻应用于Clojure习语并将命名空间视为一组函数等而不是"持有一组函数的类"或某些此类事物可能更有用.
这个一般规则有一个重要的例外:名称空间包含:gen-class在其ns表单中.这些正是为了实现一个稍后可以实例化的Java类,它可能有实例方法和每个实例状态等.注意这:gen-class是一个互操作特性 - 纯Clojure代码通常应该避免它.
或者它们应该在java中用作包吗?
它们服务于一些旨在服务的相同目的(如上所述); 类比,虽然肯定存在,但并不是那么有用,只是因为组合在一起的东西(Java类)根本不像Clojure名称空间组合在一起的东西(Clojure Vars),各种"访问级别" (private/ package/ public在Java中,{:private true}或者不在Clojure中)工作方式非常不同等.
话虽这么说,但必须记住名称空间和驻留在特定包中的包/类之间存在某种对应关系.foo.bar编译时调用的命名空间会生成一个bar在包中调用的类foo; 这尤其意味着命名空间名称应至少包含一个点,因为所谓的单段名称显然会导致类被放入"默认包"中,从而导致各种奇怪现象.(例如,我发现无法让VisualVM的探查器注意到单段命名空间中定义的任何函数.)
此外,deftype/ defrecord-created类型不驻留在名称空间中.甲(defrecord Foo [...] ...)其中命名空间中的文件中的形式foo.bar被定义创建一个名为类Foo在包foo.bar.要使用Foo来自另一个命名空间的类型,必须从包中:import获取类-/将无法工作,因为它们从命名空间中提取Vars,而不是命名空间.Foofoo.bar:use:require
因此,在这种特殊情况下,名称空间和包之间存在某种对应关系,希望利用某些较新语言功能的Clojure程序员需要注意这一点.有些人发现这给了一些"互操作风味",这些功能并不属于互操作领域(defrecord/ deftype/ defprotocol即使我们忘记了他们在JVM上实现平台速度的作用,也是一个很好的抽象机制),它是当然可能在Clojure的某个未来版本中可能会废除这种风格,因此deftype可以将&Co.的命名空间名称/包名称对应视为实现细节.
或者它们应该更普遍地用作模块系统还是什么?
它们是一个模块系统,确实应该如何使用它们.