clojure 关键字什么时候应该出现在命名空间中?

发布于 2024-08-26 14:36:19 字数 246 浏览 2 评论 0原文

在 clojure 中,关键字会自行计算,例如:

>>:test
:test

它们不接受任何参数,并且不绑定到任何东西。那么,为什么我们需要限定命名空间中的关键字呢?

我知道使用derive 创建isa 层次结构需要命名空间限定关键字(例如::test)。是否存在其他明确需要关键字位于命名空间中的情况?

In clojure, keywords evaluate to themselves, e.g.:

>>:test
:test

They don't take any parameters, and they aren't bound to anything. Why then, would we need to qualify keywords in a namespace?

I know that creating isa hierachies using derive requires namespace qualified keywords (e.g. ::test). Are there any other cases where there is a clear need for keywords to be in a namespace?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

回忆追雨的时光 2024-09-02 14:36:19

如果任何代码有机会在命名空间上下文之外与关键字交互,则应该对关键字进行命名空间限定。我能想到的主要示例是两个命名空间,将键和值放入第三个命名空间中的哈希映射中,其中键是关键字(就像 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 中的哈希映射。多方法也经常根据关键字值进行分派,并且命名空间在另一个命名空间中为多方法定义方法的情况并不少见。不难想象库的作者可能希望提供类似机制的情况。

如果您的关键字存在逃逸命名空间的风险,那么最好对您的关键字进行命名空间限定,除非您特别希望关键字破坏其他命名空间中的关键字。

You should namespace-qualify your keywords if any code is ever going to have a chance to interact with your keywords outside of the context of your namespace. The main example I can think of is two namespaces putting keys and values into a hash-map in a third namespace, where the keys are keywords (as they often are in Clojure). A contrived example:

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

Why would you want to do this? Well, some core concepts in Clojure (like derive, for example) are implemented this way, as hash-maps in clojure.core. Multimethods also often dispatch on keyword values, and it's not uncommon for a namespace to define a method for a multimethod in another namespace. It's not hard to think of situations where the author of a library might like to provide a similar sort of mechanism.

It's a good idea to namespace-qualify your keywords if there's any risk of your keywords escaping your namespace, unless you specifically want your keywords to clobber those from other namespaces.

终陌 2024-09-02 14:36:19

Clojure 目前需要命名空间限定关键字的一个地方是使用extend函数将协议的实现添加到现有类型时。 (这是 1.2 功能,可在最新快照中使用,但不可在 1.1 稳定版本中使用。)来自 (doc Extend) 的相关片段:

extend 接受一个类型/类(或接口,见下文),以及一个或多个
协议+方法映射对。它将扩展多态性
当 AType 为时调用提供的方法的协议方法
作为第一个参数提供。注意deftype类型是指定的
使用他们的关键字标签:

::MyType 或 :my.ns/MyType

事实上,对于 Apple 类型和 Eatable 协议:

(deftype Apple [colour])
(defprotocol Eatable (eat [x]))

以下抛出异常(没有实现方法::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 规范 v0.1(当今 Clojure Web 生态系统的关键部分)。请参阅发送至 Ring Google 的请求和响应映射关键字命名空间消息小组(由Ring作者制作
Mark McGranaghan)了解该设计决策背后的基本原理,以及在 Ring 规范 v0.2 中不再需要命名空间关键字的决定背后的一些见解。 James Reeves(Compojure 的作者)还发布了一条支持这一更改的消息。


最终,与所有命名空间一样,这是一种避免冲突的功能。现在编写的 Clojure 代码并不关心是否有“私有”关键字,因此它们没有多大用处;但当它们可能发挥作用时,让它们发挥作用是件好事。

One place where Clojure currently requires namespace-qualified keywords is when using the extend function function to add an implementation of a protocol to an existing type. (This is 1.2 functionality, available with the latest snapshots, but not the 1.1 stable release.) The relevant snippet from (doc extend):

extend takes a type/class (or interface, see below), and one or more
protocol + method map pairs. It will extend the polymorphism of the
protocol's methods to call the supplied methods when an AType is
provided as the first argument. Note that deftype types are specified
using their keyword tags:

::MyType or :my.ns/MyType

Indeed, for an Apple type and an Eatable protocol:

(deftype Apple [colour])
(defprotocol Eatable (eat [x]))

the following throws an exception (No implementation of method: :eat etc.):

(extend :Apple Eatable {:eat (fn [x] (println (str (name (:colour x)) ", yummy!")))})
(eat (Apple :red))

while this prints out red, yummy!:

(extend ::Apple Eatable {:eat (fn [x] (println (str (name (:colour x)) ", yummy!")))})
(eat (Apple :red))

Note I've just typed it all in at the REPL. Also note that if you want to reproduce it, it's best that you type / paste it in in the order given above; e.g. reevaluating either of the (deftype Apple [colour]) and (defprotocol Eatable (eat [x])) forms (or even both) does not make Clojure forget about the protocol implementation.

Again, this is a 1.2 feature, so it's not even there in 1.1 and might change prior to the actual release of 1.2.


Sharing a hash-map between a number of namespaces is another possible use case, as Brian says. Note that there's no need for a reference type to be involved. Say there's a bunch of libraries -- which may perhaps be added to in the future -- which manipulate (maybe transform, maybe just observe) hash-maps keyed by keywords, where each library is free to define which keywords it looks for and what it uses them for; then one might be tempted to use namespace qualified keywords to avoid collisions.

Actually, there currently exists an example which is not at all contrived, namely the Ring spec v0.1 (a key piece of the present day Clojure Web ecosystem). See the Request and response map keyword namespacing message to the Ring Google group (made by Ring author
Mark McGranaghan) for some insight into the rationale behind that design decision, as well as behind the decision to no longer require namespacing keywords in Ring spec v0.2. There's also a message by James Reeves (the author of Compojure) in support of the change.


Ultimately, as all namespacing, this is a collision avoidance feature. Clojure code that is written nowadays doesn't tend to care about having "private" keywords, so they don't see much use; but it is good to have them available for when they may make a difference.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文