2016-07-01 8 views
8

this answerを書いた後、私はspecを使用してClojure's destructuring languageを指定しようとするように促された。ハイブリッドマップをどのように指定できますか?

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

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

(s/def ::sym (s/and simple-symbol? (complement #{'&}))) 

シーケンシャル非構造部分は正規表現(私はここでそれを無視しています)との仕様に簡単ですが、私はで捕まってしまいました連想的な破壊。最も基本的なケースでは、キー式への結合フォームからマップです:

(s/def ::mappings (s/map-of ::binding ::s/any :conform-keys true)) 

しかし、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])) 

どのように作成することができたマップに::assoc仕様を作成することができます::mappingsに準拠したマップと::optsに準拠したマップを結合しますか?私はmergeがあることを知っている:

(s/def ::assoc (s/merge ::opts ::mappings)) 

しかしmergeは基本的にandの類似体であるため、これは、動作しません。私はorに類似したものを探していますが、マップについては探しています。

あなたが検証するのは簡単ですフォームにごマップを変換するために s/andにおける中間ステップとして s/conformerを使用することができます

答えて

4

{ key :key 
    :as name } 

あなたはタプルとしてマップのs/keyss/merges/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 

この簡単な配合では、コンフォーマーのアプローチよりもいくつかの利点があります。最も重要なのは、それは真実を述べている。さらに、それ以上の努力なしに生成し、適合し、非形成する必要があります。

1

(s/def ::assoc 
    (s/and 
    map? 
    (s/conformer #(array-map 
        ::mappings (dissoc % :as :or :keys :strs :syms) 
        ::opts  (select-keys % [:as :or :keys :strs :syms]))) 
    (s/keys :opt [::mappings ::opts]))) 

例えばからあなたを取得します

{ ::mappings { key :key } 
    ::opts  { :as name } } 
+0

ありがとうございました!私はこのようなものがあったと確信していました。しかし、それはかなり醜いと思われる。私は、より洗練されたソリューションが将来追加されることを願っています。 –

関連する問題