在 clojure 中 :: 有何用途?

问题描述 投票:0回答:3

我理解 Clojure 中的关键字是

:keyword
。但是
::
有什么用呢?为什么看起来像是有绑定的?

user=> :foo
:foo
user=> ::foo
:user/foo
clojure
3个回答
92
投票

双冒号的作用是用您当前的命名空间来完全限定关键字。这是为了避免对不同库有意义的关键字的名称冲突。如果没有完全限定的关键字,您可能会意外覆盖映射中的某些值并破坏与库的兼容性。


30
投票

正如现在记录的 Clojure 以及 for ClojureScript

::
关键字也可用于解析命名空间别名。例如,如果
::foo/bar
:clojure.core/bar
的别名,则
foo
将计算为
clojure.core
。如果
foo
未解析为名称空间,则会引发读取器异常。


0
投票

::
用于“完全限定的命名空间”,或者我喜欢称之为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
,别名中的点等),但关键是解决你的代码库 关于您的规则的约定并保持一致。

© www.soinside.com 2019 - 2024. All rights reserved.