在clojure中,关键字会评估自己,例如:
>>:test
:test
它们不接受任何参数,并且它们不受任何限制。那么,为什么我们需要在命名空间中限定关键字?
我知道使用isa
创建derive
层次结构需要使用命名空间限定的关键字(例如::test
)。是否存在明确需要关键字在命名空间中的其他情况?
答案 0 :(得分:24)
如果任何代码有机会在命名空间的上下文之外与您的关键字进行交互,则应该对命名空间进行命名空间限定。我能想到的主要示例是两个名称空间,它们将键和值放入第三个名称空间中的哈希映射中,其中键是关键字(因为它们通常在Clojure中)。一个人为的例子:
user> (ns main)
nil
main> (def DATA (ref {}))
#'main/DATA
main> (ns foo)
nil
foo> (dosync (alter main/DATA assoc :bad 123 ::good 123))
{:foo/good 123, :bad 123}
foo> main/DATA
#<Ref@541b02: {:foo/good 123, :bad 123}>
foo> (ns bar)
nil
bar> (dosync (alter main/DATA assoc :bad 456 ::good 456))
{:bar/good 456, :foo/good 123, :bad 456} ;; oops, no more :bad from foo
你为什么要这样做?好吧,Clojure中的一些核心概念(例如derive
)以这种方式实现,作为clojure.core
中的哈希映射。 Multimethods也经常调度关键字值,并且命名空间为另一个命名空间中的多方法定义方法的情况并不少见。不难想象图书馆作者可能希望提供类似机制的情况。
如果您的关键字有逃避命名空间的风险,最好对命名空间进行命名空间限定,除非您特别希望关键字能够破坏其他命名空间中的关键字。
答案 1 :(得分:11)
Clojure当前需要命名空间限定关键字的一个地方是使用extend
函数函数将协议的实现添加到现有类型。 (这是1.2功能,可用于最新快照,但不适用于1.1稳定版本。)(doc extend)
的相关摘录:
extend接受一个类型/类(或接口,见下文),以及一个或多个 协议+方法映射对。它将扩展多态性 协议的方法在AType时调用提供的方法 作为第一个参数提供。请注意,指定了deftype类型 使用他们的关键字标签:
:: MyType或:my.ns / MyType
确实,对于Apple类型和Eatable协议:
(deftype Apple [colour])
(defprotocol Eatable (eat [x]))
以下引发异常(No implementation of method: :eat
等):
(extend :Apple Eatable {:eat (fn [x] (println (str (name (:colour x)) ", yummy!")))})
(eat (Apple :red))
虽然打印出red, yummy!
:
(extend ::Apple Eatable {:eat (fn [x] (println (str (name (:colour x)) ", yummy!")))})
(eat (Apple :red))
注意我刚刚在REPL中输入了所有内容。另请注意,如果要重现它,最好按上面给出的顺序键入/粘贴它;例如重新评估(deftype Apple [colour])
和(defprotocol Eatable (eat [x]))
表单(甚至两者)都不会让Clojure忘记协议的实现。
同样,这是一个1.2功能,所以它在1.1中甚至不存在,并且可能在实际发布1.2之前发生变化。
在众多命名空间之间共享哈希映射是另一种可能的用例,正如Brian所说。请注意,不需要涉及引用类型。假设有一堆库 - 可能会在将来添加 - 操纵(可能转换,也许只是观察)由关键字键控的哈希映射,其中每个库可以自由定义它查找的关键字和什么它用它们;然后人们可能会试图使用命名空间限定的关键字来避免冲突。
实际上,目前存在一个完全没有做过的例子,即Ring spec v0.1(当今Clojure Web生态系统的关键部分)。请参阅Ring Google小组的Request and response map keyword namespacing消息(由Ring作者制作) Mark McGranaghan)了解了该设计决策背后的基本原理,以及决定不再需要Ring spec v0.2中的命名空间关键字。 James Reeves(Compojure的作者)也支持这一变化。
最终,作为所有命名空间,这是一个避免碰撞的功能。如今编写的Clojure代码并不倾向于关注“私有”关键字,因此它们没有太多用处;但是当它们有所作为时,最好让它们可用。