我理解 Clojure 中的关键字是
:keyword
。但是::
有什么用呢?为什么看起来像是有绑定的?
user=> :foo
:foo
user=> ::foo
:user/foo
双冒号的作用是用您当前的命名空间来完全限定关键字。这是为了避免对不同库有意义的关键字的名称冲突。如果没有完全限定的关键字,您可能会意外覆盖映射中的某些值并破坏与库的兼容性。
正如现在记录的 Clojure 以及 for ClojureScript,
::
关键字也可用于解析命名空间别名。例如,如果 ::foo/bar
是 :clojure.core/bar
的别名,则 foo
将计算为 clojure.core
。如果 foo
未解析为名称空间,则会引发读取器异常。
::
用于“完全限定的命名空间”,或者我喜欢称之为FQNS
它在概念上类似于
FQDN。它(::
)
“扩展”为“必需的别名”(例如,::str
是点线的别名)
(:require [clojure.string :as str])
的一部分),或者它所在的当前 NS(例如,
::
是当前文件顶部 (ns myproj.myns ...)
的别名)。
FQNS 可以采用多种形状,并且应该与 使用
:
的基本“非限定”关键字语法。两种关键字语法
(完全限定和基本)可以使用前导冒号和嵌套点和
斜杠以有意义的方式表示包含键的命名空间。 (你
另请参阅常用的 -
,但这与其他任何内容都相同
字母数字。)这些点是低级细节,但代表了层次结构
父/子命名空间,其中 aa.bb
是 aa
作为父,bb
作为子。
我发现让你的编辑清楚地表明你的体型很有帮助 观察。您可以在屏幕截图中看到,
::
是 red
当前 NS,显式完整 NS 为 blue,“别名”NS 为 green,并且
最后的“钥匙”是紫色。
注意这个例子(你可以在 REPL 中使用)这些是如何“解析”的 (注意后面的评论):
(ns proj.ns1)
(ns proj.area.ns2)
(ns proj.ns3
(:require
[clojure.string :as str]
[clojure.data.avl :as d-a] ; or da or avl or just a; be consistent across code base
[clojure.data.zip :as d.z] ; using dots in alias is confusing IMO
[proj.area.ns2 :as ns2] ; some fns being used
[proj.ns1 :as-alias ns3])) ; for keying convenience, new in v1.11
(def m "A contrived map demonstrating various key shapes"
{:aa "good" ;=> :aa ; typical: basic unqualified key
::bb "good" ;=> :proj.ns1/bb ; typical: note that / is implicitly added
:ns3/cc "weird" ;=> :ns3/cc ; missing full ns despite alias
:proj.area.ns4/dd "ok" ;=> :proj.area.ns4.dd ; ns might not exist but ok
::ns2/ff "good" ;=> :proj.area.ns2/ff ; common practice
:proj.area.ns2/gg "ok" ;=> :proj.area.ns2/gg ; but this is why we have `:as-alias`
:proj.ns1/hh "bad" ;=> :proj.ns1/hh ; clearer to just use `::` for cur ns
:str/ii "bad" ;=> :str/ii ; an accident: str not expanded
::str/jj "good" ;=> :clojure.string/jj ; and typical
::kk.ll "sure" ;=> :proj.ns1/kk.ll ; confusing to have dots in actual key
::d-a/mm.nn "sure" ;=> :clojure.data.json/mm.nn ; again, dots in key
::d-a/oo "good" ;=> :clojure.data.json/oo ; typical
::d.z/pp "weird" ;=> :proj.ns1/pp ; dots in qualifier diff from ns structure
:random/qq "good" ;=> :random/qq ; qualified, but not a real ns
:other.random/rr "good" ;=> :other.random/rr ; qualified, but not a real ns
})
请注意,这些都是您正在制作的新密钥。
:jj
键实际上并不
存在于 clojure.string
NS 中,但这并不妨碍您使用它。
当您使用库时,了解大多数 FQNS 案例非常重要 像 malli 或 规格或 积分 或其他各种,因为 他们大量使用 FQNS。对于任何大型项目,通常 有必要限定某些键以避免冲突。
注意在解构这些时使用
:keys
。
(let [{:keys [aa]} m] aa) ; "good"
(let [{:keys [:aa]} m] aa) ; "good"
(let [{:keys [::aa]} m] aa) ; nil
(let [{:keys [::bb]} m] bb) ; "good"
(let [{:keys [ns2/ff]} m] ff) ; nil
(let [{:keys [:ns2/ff]} m] ff) ; nil
(let [{:keys [::ns2/ff]} m] ff) ; "good"
(let [{:keys [ns3/cc]} m] cc) ; "weird"
(let [{:keys [:ns3/cc]} m] cc) ; "weird"
(let [{:keys [other.random/rr]} m] rr) ; "good"
(let [{:keys [:other.random/rr]} m] rr) ; "good"
这是 Clojure 语法中需要保持直线的一个棘手区域,但它是一个 日常代码的重要组成部分。如果您遵循 NS,将有助于减少混乱 诸如此类的风格指南:
请注意,这里关于别名约定可能存在分歧(
str
与
string
,别名中的点等),但关键是解决你的代码库
关于您的规则的约定并保持一致。