键* /带内联值规格的键

时间:2016-12-20 09:59:41

标签: clojure clojure.spec

我想用keys / keys*编写一个规范,但能够内联值规范,这是不受支持的by design,我得到了它背后的推理。但是,当地图有特定的上下文时,有时候想要(或者只是通过传统或第三方)键和值之间的耦合。

我还不熟悉规范,这只是我第一次将它与现有项目集成,并且它不断给我带来问题,因为它假设太多,特别是因为上面提到的原因。例如。想象一个描述时间段的地图,并且有一个日期的until键,并且在同一个ns中有一个列表处理的地图,并且有一个until采用谓词函数。我现在需要手动编写完全命名空间的密钥用于甚至不存在的命名空间(alias很可爱,但它必须在多个命名空间/文件中不断复制)。除了烦躁之外,我觉得它也容易出错。

另一个keys / keys*假设太多的地方,如果我甚至想要关键字作为我的密钥。我正在为非程序员编写DSL但是现在正在为技术用户编写,最重要的是我想要用符号作为键来指定地图。似乎没有任何支持。

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

2 个答案:

答案 0 :(得分:4)

您可以使用map-of

来指定带符号的地图
(s/def ::sm (s/map-of symbol? any?))

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

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

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

(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

答案 1 :(得分:1)

  

我现在需要手动编写完全命名空间的密钥   甚至不存在的名称空间

我也一直在使用这种方法,我认为我更喜欢它,而不是确保关键字的命名空间始终与真正的Clojure NS形式相对应。我使用:business-domain-concept/a-name之类的关键字而不是:my-project.util.lists/a-name

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

听起来你已经意识到这种任意关键字命名空间方法 - 特别是,我买它感觉容易出错,因为你手动输入这些东西并且spec似乎没有窒息如果你意外地喂了s/keys :fate/until。但是,FWIW,我认为你现在感觉到命名空间关键字特别想要解决的痛苦:你在一个Clojure文件中,你有两个名为until的密钥的地图,它们完全意味着两个不同的事情。

  

我正在为非程序员编写DSL但是现在是技术用户,   并且底线是我想要用符号作为键来规划地图。

我认为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