Using s/& and Mark’s fdef does “work” but the failure messages seem misleading:
boot.user=> (f 2 :even 4) nil boot.user=> (f 2 :even 3) clojure.lang.ExceptionInfo: Call to #'boot.user/f did not conform to spec: val: () fails at: [:args :options] predicate: (& (* (cat :clojure.spec/k keyword? :clojure.spec/v :clojure.spec/any)) :clojure.spec/kvs->map (keys :opt-un [:boot.user/even])), Insufficient input :clojure.spec/args (2 :even 3) :clojure.spec/failure :instrument-check-failed Why Insufficient input here? Why doesn’t it point at 3 failing the ::even or even? predicate? boot.user=> (f 2 :even 4 :odd 5) clojure.lang.ExceptionInfo: Call to #'boot.user/f did not conform to spec: val: {:even 4, :odd 5} fails at: [:args :options] predicate: (fn [m] (not (contains? m :odd))) :clojure.spec/args (2 :even 4 :odd 5) :clojure.spec/failure :instrument-check-failed This is fine: we see it fails the specific predicate provided. boot.user=> (f 2 :even 4 :o 5) nil Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN An Architect's View -- http://corfield.org/ "If you're not annoying somebody, you're not really alive." -- Margaret Atwood On 6/29/16, 5:07 PM, "Alex Miller" <clojure@googlegroups.com on behalf of a...@puredanger.com> wrote: As soon as you introduce the s/and, you have dropped out of regex land and into a predicate. You are then matching something in a new nested regex inside the and like: [[::even 4]]. To stay within the top-level regex and supply extra predicates, use s/& instead: (s/def ::options (s/& (s/keys* :opt-un [::even]) (fn [m] (not (contains? m :odd))))) user=> (s/conform ::options [:even 2]) {:even 2} user=> (s/conform ::options [:even 2 :foo 5]) {:even 2, :foo 5} user=> (s/explain ::options [:even 3]) val: () fails spec: :user/options predicate: (& (* (cat :clojure.spec/k keyword? :clojure.spec/v :clojure.spec/any)) :clojure.spec/kvs->map (keys :opt-un [:user/even])), Insufficient input user=> (s/explain ::options [:even 2 :odd 3]) val: {:even 2, :odd 3} fails spec: :user/options predicate: (fn [m] (not (contains? m :odd))) On Wednesday, June 29, 2016 at 5:19:09 PM UTC-5, puzzler wrote: I'm having trouble spec'ing out something like this, a function that takes an integer as an input followed by a series of optional keyworded args. :even is an allowed optional keyword, but we definitely want to forbid :odd as an optional keyword. (s/def ::even even?) (s/def ::options (s/and (s/keys* :opt-un [::even]) (fn [m] (not (contains? m :odd))))) (defn f [n & {:as options}] nil) (s/fdef f :args (s/cat :integer int? :options ::options)) (stest/instrument `f) This doesn't work at all and gives all sorts of errors when f is called with any input. I believe it is because the use of s/and in the definition of ::options interferes with the ability of ::options to be "flattened" into the s/cat definition. My reasoning: ::options correctly validates [:even 2] and rejects [:even 2 :odd 3] and [:even 3]. If I omit the s/and and the second clause so that it reads: (s/def ::options (s/keys* :opt-un [::even])) this also behaves as expected. So my conclusion is that the s/and is interfering with the ability of s/keys* to sit within the s/cat definition. How does one solve this problem? -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.