我如何规划混合地图?

Sam*_*tep 8 clojure destructuring clojure.spec

写完这个答案之后,我受到启发,尝试使用以下方法指定Clojure的解构语言spec:

(require '[clojure.spec :as s])

(s/def ::binding (s/or :sym ::sym :assoc ::assoc :seq ::seq))

(s/def ::sym (s/and simple-symbol? (complement #{'&})))
Run Code Online (Sandbox Code Playgroud)

顺序解构部分很容易用正则表达式进行规范(所以我在这里忽略它),但我陷入了联想解构.最基本的情况是从绑定表单到键表达式的映射:

(s/def ::mappings (s/map-of ::binding ::s/any :conform-keys true))
Run Code Online (Sandbox Code Playgroud)

但Clojure也提供了几个特殊键:

(s/def ::as ::sym)
(s/def ::or ::mappings)

(s/def ::ident-vec (s/coll-of ident? :kind vector?))
(s/def ::keys ::ident-vec)
(s/def ::strs ::ident-vec)
(s/def ::syms ::ident-vec)

(s/def ::opts (s/keys :opt-un [::as ::or ::keys ::strs ::syms]))
Run Code Online (Sandbox Code Playgroud)

如何::assoc为可以通过将符合::mappings的地图和符合的地图合并在一起创建的地图创建规范::opts?我知道有merge:

(s/def ::assoc (s/merge ::opts ::mappings))
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为merge它基本上是类似的and.我正在寻找类似于or地图的东西.

Ale*_*ler 5

您可以使用地图s/merges/keyss/every作为元组来定义混合地图.这是一个更简单的例子:

(s/def ::a keyword?)
(s/def ::b string?)
(s/def ::m
  (s/merge (s/keys :opt-un [::a ::b])
           (s/every (s/or :int (s/tuple int? int?)
                          :option (s/tuple keyword? any?))
                    :into {})))

(s/valid? ::m {1 2, 3 4, :a :foo, :b "abc"}) ;; true
Run Code Online (Sandbox Code Playgroud)

与配方器方法相比,这种更简单的配方具有几个优点.最重要的是,它说明了事实.此外,它应该生成,符合和不成形,而无需进一步努力.