键*/具有内联值规范的键

keys*/keys with inlined value specs

我想用 keys/keys* 编写规范,但能够内联值规范,这是不支持的 by design,我明白了背后的原因。然而,有时您确实想要(或者只是通过遗留或第 3 方拥有)当地图有特定上下文时键和值之间的耦合。

我对规范还是陌生的,这只是我第一次将它与现有项目集成,它不断给我带来问题,因为它假设太多,尤其是由于上述原因。例如。想象一个描述时间段并具有日期的 until 键的映射,并且在同一个 ns 中有一个用于列表处理的映射,并且有一个带有谓词函数的 until。我现在需要为甚至不存在的命名空间手动编写完全命名空间的键(aliasing 很可爱,但它必须在多个 namespaces/files 中不断复制)。除了烦人之外,我觉得它也容易出错。

另一个 keys/keys* 假设太多的地方是如果我什至想要关键字作为我的键。我正在为非程序员但现在是技术用户编写 DSL,最重要的是我想指定一个以符号作为键的映射。这似乎不受任何支持。

有什么我没有得到的吗?还是规范真的缺少基本功能?

I now need to mess with manually writing fully namespaced keys for namespaces that don't even exist

我也一直在使用这种方法,我想我实际上更喜欢它,而不是喜欢确保关键字的名称空间始终对应于真正的 Clojure NS 表单。我使用 :business-domain-concept/a-name 之类的关键字而不是 :my-project.util.lists/a-name.

您可以使用不映射到任何 Clojure NS 的任意命名空间来创建关键字。例如,在您的 until 情况下,您可以定义描述日期的 :date/until 规范,以及描述列表处理地图的(也许有更好的名称):list/until 规范领域。

听起来您已经知道这种任意关键字命名空间的方法 - 特别是,我认为它容易出错,因为您是手动输入这些内容并且规范没有出现如果您不小心喂 s/keys 一个 :fate/until 会窒息。不过,FWIW,我认为您目前正感受到命名空间关键字专门用于解决的痛苦:您在一个 Clojure 文件中,您有两个带有名为 until 键的映射,它们完全意味着两个不同的东西。

I'm writing a DSL for non-programmers but technical users right now, and bottom line is that I want to spec a map with symbols as keys.

我认为 map-of 是您想要的:

user=> (s/def ::valid-symbols #{'foo 'bar 'baz})
:user/valid-symbols
user=> (s/def ::symbol-map (s/map-of ::valid-symbols int?))
:user/symbol-map
user=> (s/valid? ::symbol-map {'foo 1 'bar 3})
true
user=> (s/valid? ::symbol-map {'foo 1 'quux 3})
false

您可以使用 map-of:

指定一个以符号作为键的地图
(s/def ::sm (s/map-of symbol? any?))

或通过将地图指定为条目集合:

(s/def ::sm (s/every (s/tuple symbol? any?) :kind map? :into {}))

后者特别有趣,因为您可以 s/or 许多不同种类的元组来描述更有趣的地图,而不是单个元组。您甚至可以通过这种方式将这些符号连接到其他现有规范:

(s/def ::attr1 int?)
(s/def ::attr2 boolean?)
(s/def ::sm (s/every (s/or :foo (s/tuple #{'foo} ::attr1)
                           :bar (s/tuple #{'bar} ::attr2))
              :kind map? :into {}))
(s/valid? ::sm {'foo 10 'bar true}) ;; => true