spec 的价值主张之一是它不尝试定义实际的模式。它不会将实体的定义绑定到其组件的定义。引用规范原理:
大多数用于指定结构的系统将键集的规范(例如,映射中的键、对象中的字段)与这些键指定的值的规范混为一谈。即在这种方法中,地图的模式可能会说:a-key 的类型是 x-type,而 :b-key 的类型是 y-type。这是僵化和冗余的主要来源。
在 Clojure 中,我们通过动态组合、合并和构建地图来获得力量。我们经常处理可选和部分数据、由不可靠的外部源产生的数据、动态查询等。这些映射表示相同键的各种集合、子集、交集和并集,并且通常对于相同的键应该具有相同的语义。它被使用。定义每个子集/联合/交集的规范,然后冗余地说明每个键的语义,这在大多数动态情况下都是反模式和不可行的。
所以直接回答这个问题,不,spec 没有提供这种类型的规范,因为它是专门设计的。您在类似于模式的定义中牺牲了一定程度的人类可读性,以获得更动态、可组合、更灵活的规范。
尽管这不是您的问题,但请考虑使用将实体的定义与其组件的定义分离的系统的好处。这是人为的,但考虑定义一辆车(在这里保持简单以节省空间,只使用轮胎和底盘):
(s/def ::car (s/keys :req [::tires ::chassis]))
我们定义一次,我们可以在其上放置任何我们想要的轮胎配置:
(s/def ::tires (s/coll-of ::tire :count 4))
(s/def ::tire (s/or :goodyear ::goodyear}
:michelin ::michelin))
(s/def ::goodyear #{"all-season" "sport" "value"})
(s/def ::michelin #{"smooth ride" "sport performance"})
(s/def ::chassis #{"family sedan" "sports"})
以下是不同的配置,但都是有效的汽车:
(s/valid? ::car {::tires ["sport" "sport" "sport" "sport"]
::chassis "sports"})
(s/valid? ::car {::tires ["smooth ride" "smooth ride"
"smooth ride" "smooth ride"]
::chassis "family sedan"})
这是人为的,但很明显,将组件定义为与组件组合在一起形成的组件分开是很灵活的。轮胎有自己的规格,它们的规格并不是汽车的定义,即使它们是汽车的部件。它更冗长,但更灵活。