中分享您的想法!

欢迎!请参阅关于页面,以获取更多关于其工作原理的信息。

0
Spec
编辑

我需要规范类似map-entry的向量,其键本身也是经过规范化的。每个向量看起来像这样

[:my.spec-ed.key/foo 42]

具体的键有所变化,我希望编写一个谓词,可以基于其键动态地规范类似map-entry的向量,就像通过(s/keys)逐条规范映射条目一样。

在这种情况下,假设键是这样的规范

(s/def :my.spec-ed.key/foo int?)

我能想到的唯一方式来规范类似map-entry的向量是这样

(s/def :my.spec-ed.key/pair (s/and vector? #(s/valid? (first %) (second %))))

这没问题,但我不喜欢这样做,因为当出现问题的时候,(s/explain) 并不是很好。例如(s/explain :my.spec-ed.key/foo "foo")是很有信息的

"foo" - failed: int? spec: :my.spec-ed.key/foo

相反,(s/explain :my.spec-ed.key/pair [:my.spec-ed.key/foo "bar"])没有解释真正犯了什么错误(我传了一个字符串而不是整数),我不得不查找原始规范以找出原因

[:my.spec-ed.key/foo "bar"] - failed: (valid? (first %) (second %)) spec: :my.spec-ed.key/pair

有人有什么更好的方法来规范我的类似map-entry的向量吗?值得一提的是,这些向量实际上并不来自映射,也不打算映射到映射,所以不能使用专门为了在映射上工作的规范函数。

2 个答案

+1
作者:
选定 作者:
 
最佳答案

对于固定长度的内容,元组通常是最佳选择...

(s/tuple #{:my.spec-ed.key/foo} :my.spec-ed.key/foo)

作者:
非常感谢您在周六晚上如此迅速地回答Alex。

恐怕我没有很好地表达我的问题,所以我编辑了我的问题。

我在寻找一个能够与任意指定键的谓词一起使用的谓词,就像spec能够与map键和(s/keys)一起使用一样。我给出的例子在不完美的情况下达成了这个目标,使用谓词内的s/valid?。使用您的解决方案,除非我遗漏了什么,否则针对我的数十个键规范,我可能需要为每个规格编写一个相应的元组规范。是这样吗?
作者:
每个规格可能都需要这样做,但宏是去除这种乏味工作的绝佳工具,所以我认为这并不是一个限制因素。

另一个选择是使用s/keys*,它用于kv参数的顺序集合。例如,`(s/keys* :req [:my.spec-ed.key/foo])`。如果您想将其限制为仅为向量和单个对,则可以使用`(s/and vector? #(= 2 (count %)) (s/keys* :req [:my.spec-ed/key]))`。
0

编辑

多亏了 @alexmiller 的回答,我才能定位到这个具体的解决方案

(s/def :my.spec-ed.key/pair (s/and vector? #(= 2 (count %)) (s/keys*)))

这将验证任何先前已指定键的列表。

当然,正如Alex指出,我们总是可以编写宏,这个宏对我来说有效,它为命名空间中的每个规范创建相应的-tuple规范,然后将它们全部合并成一个主"pairs"规范。想法是您只需在每个规范定义后运行一次,因为它本身不会过滤出它自己创建的规范。

(defmacro pairs-spec
  [spec-id]
  (let [spec-ns (namespace spec-id)
        ors (->> (s/registry)
                 (map first)
                 (filter #(= spec-ns (namespace %)))
                 (mapcat (fn [ky] `(~(keyword spec-ns
                                              (str (name ky)
                                                   "-tuple"))
                                    (s/tuple #{~ky} ~ky)))))]
    `(s/def ~spec-id (s/or ~@ors))))

(pairs-spec :my.spec-ed.key/pair)

使用(s/explain)结果并不理想,因为它构建了一个大的(s/or)语句,其中每个条目对应命名空间中每个先前指定的键。由于这一点,以及我不想在宏运行时担心是否所有键都已被指定,我更倾向于上面的(s/keys*)解决方案。

非常感谢帮助!!

...