This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-10-23
Channels
- # 100-days-of-code (2)
- # aws (1)
- # beginners (105)
- # boot (36)
- # calva (4)
- # cider (56)
- # clara (37)
- # cljdoc (16)
- # cljs-dev (19)
- # clojure (44)
- # clojure-dev (20)
- # clojure-italy (24)
- # clojure-nl (3)
- # clojure-serbia (2)
- # clojure-spec (15)
- # clojure-uk (44)
- # clojurescript (41)
- # code-reviews (3)
- # core-async (12)
- # cursive (24)
- # datomic (4)
- # emacs (1)
- # figwheel-main (10)
- # fulcro (168)
- # funcool (2)
- # hyperfiddle (15)
- # jobs (2)
- # jobs-discuss (79)
- # juxt (19)
- # lein-figwheel (1)
- # leiningen (2)
- # luminus (14)
- # mount (8)
- # nrepl (9)
- # off-topic (9)
- # other-languages (1)
- # pathom (32)
- # reitit (6)
- # ring-swagger (3)
- # shadow-cljs (10)
- # slack-help (11)
- # spacemacs (20)
- # sql (29)
- # tools-deps (28)
- # vim (29)
- # yada (4)
There’s a general structure and spec, but also more specific specs which must nevertheless maintain the structure
Hey folks, I'm having a tough time trying to figure out how multi-specs interact with unqualified keys - here's some example code which is a trivial version of what I'm trying to achieve:
(def mode? #{:dog :cat})
(s/def ::cat string?)
(s/def ::dog string?)
(s/def ::mode mode?)
(s/def ::dog-o (s/keys :req-un [::mode
::dog]))
(s/def ::cat-o (s/keys :req-un [::mode
::cat]))
(defmulti animode ::mode)
(defmethod animode :dog [_] ::dog-o)
(defmethod animode :cat [_] ::cat-o)
(s/def ::animal (s/multi-spec animode ::mode))
then some repl: user> (s/explain ::animal {:cat "edward" :mode :cat})
val: {:cat "edward", :mode :cat} fails spec: :user/animal at: [nil] predicate: animode, no method
=> nil
user> (s/explain ::animal {:cat "edward" ::mode :cat})
val: {:cat "edward", :user/mode :cat} fails spec: :user/cat-o at: [:cat] predicate: (contains? % :mode)
=> nil
any guidance as to how I square this apparent circle?@j0ni Your defmulti
and s/multi-spec
should have :mode
, not ::mode
. You want a function that operates on your data and returns the discriminant. That's the keyword :mode
, not the spec ::mode
.
(def mode? #{:dog :cat})
(s/def ::cat string?)
(s/def ::dog string?)
(s/def ::mode mode?)
(s/def ::dog-o (s/keys :req-un [::mode
::dog]))
(s/def ::cat-o (s/keys :req-un [::mode
::cat]))
(defmulti animode :mode)
(defmethod animode :dog [_] ::dog-o)
(defmethod animode :cat [_] ::cat-o)
(s/def ::animal (s/multi-spec animode :mode))
then user=> (s/explain ::animal {:cat "edward" :mode :cat})
Success!
nil
wow, thanks @seancorfield! I had been through this permutation, but apparently evaluating the ns is insufficient, I needed to reload the namespace completely
A trick with defmulti
is to put (def animode nil)
above it and then it should work with just a re-eval of the ns.
I was surprised by this behavior in spec. Is there a reason that I shouldn't expect this to work?
(gen/generate (s/gen #{:works})) => :works
(gen/generate (s/gen #{nil})) => clojure.lang.ExceptionInfo Couldn't satisfy such-that predicate after 100 tries.
I know I can do (gen/generate (s/gen nil?))
to get around this, but I found this while writing some code for handling generic generator overridesI guess I can take generators out of the equation and get to the crux of the issue:
(s/valid? #{nil} nil) => false
(s/valid? #{false} false) => false
the spec just calls the set as a function and (#{nil} nil) => nil
in clojure and spec is looking for the function truthiness. The only way to make this work would be to change the way spec deals with sets as specs where spec could call them with (contains? #{nil} nil)
instead. But that sort of special casing might be undesirable.I'm trying to come up with a spec for nested associative structures:
(s/def ::nested-associative
(s/or :coll (s/coll-of (s/or :branch ::nested-associative
:leaf string?)
:min-count 0
:max-count 1
:gen-max 1)
:map (s/map-of keyword? (s/or :branch ::nested-associative
:leaf string?)
:min-count 0
:max-count 1
:gen-max 1)))
;; in the repl:
> (gen/sample (s/gen ::nested-associative) 100)
clojure.lang.ExceptionInfo: Unable to construct gen at: [:map 1 :branch :map 1 :branch :coll :branch :coll :branch] for: :user/nested-associative
Does anyone know why this would be crashing randomly? Alternatively, is there maybe a better way to do this?