2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请参阅关于页面了解更多关于如何使用本站的信息。

0
头像 Spec
编辑

我需要规范键也是规范过的映射类似向量。每个向量看起来像这样

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

具体键的使用可能不同,我希望能写一个谓词,该谓词可以根据键动态规范任何映射类似向量,就像通过 (s/keys) 对映射逐条进行规范一样。

在这种情况下,让我们假设键这样规范

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

我可以想到的最简单的规范映射类似向量的方法是这个

(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

有人有什么更好的方法来规范我的映射类似向量吗?就这一点而言,这些向量实际上并不是来自映射,也不会映射到任何地方,所以我不能使用为专门适用于映射的规范函数。

2 个答案

+1

选定
 
最佳答案

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

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

非常感谢您在周六晚上花时间这么快地回答 Alex。

恐怕我没有很好地传达我的问题,并已编辑了我的问题。

我在寻找一个与任意规格化键一起工作的谓词,就像规格与地图键和 (s/keys) 一起工作一样。我给出的例子使用了谓词中的 s/valid? 来实现这一目标,尽管不够完美。使用您的解决方案,除非我遗漏了什么,否则我需要为我的数十个键规格编写相应的元组规格。是这样吗?
您需要为每个规格都这样做,但宏是消除这种枯燥任务的一个很好的工具,所以我认为这不会是一个限制因素。

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

感谢@alexmiller的回答,使我能够聚焦于这个特定的解决方案

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

这将验证任何之前指定的键

当然,正如Alex所指出的,人们总是可以编写宏,这个对我有效,这是一个宏,为命名空间中的每个spec创建相应的-tuple spec,然后将它们全部结合成一个主“pairs” spec。想法是你只在定义其他spec后运行它一次(它不会过滤掉它自己创建的spec)

(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/or)语句,其中一个条目对应于命名空间中每个已指定的键,不会从(s/explain)中获得好的结果。因此,我不愿意在宏运行时担心我的所有键是否都已经指定,所以我更喜欢上面的(s/keys*)解决方案。

感谢帮助!!

...