wliao
wliao

Reputation: 1416

* regular expression operator in clojure.spec

According to Spec Guide

* regular expression operator: checks 0 or more of a predicate/pattern, as this:

(s/def ::seq-of-keywords (s/* keyword?))

;; opts are alternating keywords and booleans
(s/def ::opts (s/* (s/cat :opt keyword? :val boolean?)))
(s/conform ::opts [:silent? false :verbose true])
;;=> [{:opt :silent?, :val false} {:opt :verbose, :val true}]

But as I understand it, s/cat checks a sequence.

So why (s/* (s/cat)) is not for checking a sequence of sequence each of which conforms to (s/cat).

Something like this:

(s/conform ::opts [[:silent? false] [:verbose true]])

Why does it act like s/* flattened s/cat? Or How can I check for something like [[:silent? false] [:verbose true]]?

Upvotes: 2

Views: 560

Answers (2)

Thumbnail
Thumbnail

Reputation: 13483

Why does ... s/* flatten s/cat?

... because a regular expression is a structured specification of a flat sequence. To paraphrase the Spec Guide to Sequences ...

  • When regex ops are combined, they describe a single sequence.
  • To spec a nested sequence, wrap it in an explicit call to s/spec.

How can I spec something like [[:silent? false] [:verbose true]]?

To spec a sequence of pairs, each of which is a keyword followed by a boolean:

(s/def ::opts (s/* (s/spec (s/cat :opt keyword? :val boolean?))))

Note the s/spec wrapping the s/cat. Now, for example, ...

=> (s/conform ::opts [[:silent? false] [:verbose true]])
[{:opt :silent?, :val false} {:opt :verbose, :val true}]

You can, of course, as akond does, use

  • s/tuple instead of s/catand
  • s/coll-of instead of s/*

with no need for the interposed s/spec.

Upvotes: 0

akond
akond

Reputation: 16060

You do that by using coll-of:

(s/conform (s/coll-of ::opts) [[:silent? false] [:verbose true]])
=> [[{:opt :silent?, :val false}] [{:opt :verbose, :val true}]]

Sometimes it helps to generate sample data:

(gen/generate (s/gen ::opts))
=> (:aqfR6b*C/.  false  :?.03/Vu7?  false  :Y17UL0/McsI5h  true)

which does not match the pattern of [[:silent? false] [:verbose true]]

But this one should be probably closer to what you are looking for:

(gen/generate (s/gen (s/coll-of (s/tuple keyword? boolean?))))
=> [[:X_o.u?7i/o.dIgTy false]  [:L?*/_WY._:z true]  [:X26:-j/l2q!u-7I false]]

Upvotes: 1

Related Questions