This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-11-28
Channels
- # announcements (1)
- # beginners (205)
- # calva (30)
- # cider (5)
- # cljdoc (25)
- # cljs-dev (2)
- # clojure (119)
- # clojure-brasil (5)
- # clojure-conj (7)
- # clojure-europe (2)
- # clojure-hamburg (7)
- # clojure-italy (14)
- # clojure-nl (2)
- # clojure-russia (13)
- # clojure-spec (79)
- # clojure-uk (58)
- # clojurescript (54)
- # core-logic (2)
- # core-matrix (2)
- # cursive (40)
- # datascript (2)
- # datomic (18)
- # duct (2)
- # emacs (14)
- # figwheel (3)
- # figwheel-main (7)
- # fulcro (30)
- # funcool (1)
- # graphql (10)
- # jobs (1)
- # juxt (13)
- # lumo (1)
- # mount (1)
- # off-topic (56)
- # other-languages (2)
- # pedestal (17)
- # powderkeg (2)
- # protorepl (2)
- # re-frame (10)
- # reagent (1)
- # reitit (7)
- # ring-swagger (10)
- # schema (2)
- # shadow-cljs (70)
- # spacemacs (13)
- # specter (4)
- # sql (9)
- # tools-deps (26)
in the geo-relevant ns check with a ::geo-users and in the other with ::email-users
@urzds and "structure" means "value of the keyword"? so you want shape (keys set) of the maps to be the same, but content (vals) be different?
e.g. {::foo 1}
and {::foo "str"}
?
If yes, and if you are limiting yourself to ::
- just define everything (key specs and map specs) in 2 different namespaces. You'd have to define map specs twice (for your X and Y cases), since ::name
will resolve to different keywords in each namespace: :my.ns.x/name
and :may.ns.y/name
, so your map specs would look similar, but resolve/expand into different ones, depending on namespace they are in: (s/keys :req [::name])
-> (s/keys :req [:my.ns.x/name])
and (s/keys :req [:my.ns.y/name])
.
I would not chase the goal to save few lines of text here, and just define it "twice". At least you'll keep "go to spec definition" feature or your IDE working.
@misha The other way round: The values should have the same requirements, but the structure / shape / keys required may be different.
I always thought that if I (s/def ::foo ...)
, and validate a map that contains this exact fully qualified keyword, then spec would pick up the s/def
automatically. And that this would stop working if the namespace part of the keyword would be different.
That's why I assumed that I can only define one map structure (at least using spec how I understood it), because eventually spec will validate the keys inside that map using the global definition for that key, instead of using the local structure that I would like to use in that specific place.
Or will that start to work if I use :req-un
/ :opt-un
instead of :req
and :opt
? I.e. can I define multiple structures / shapes using the -un
variants, because the match in the map only uses the keyword name, but the structural / shape validation happens using the namespace+name? So a structure / shape that is defined using :req
and :opt
is enforced everywhere and defines the complete data structure, but if it is defined :req-un
and :opt-un
, other definitions might exist in other places that have different structural / shape requirements?
@urzds Not sure I follow the convo here, but s/keys is about validating the presence and absence of keys on a map
Given this data: {::foo {::bar 1 ::baz 2}}
, how would you validate it two times using spec, such that for the first time {::foo {::bar ...}}
is required and the 2nd time {::foo {::baz ...}}
is required?
you either have to give up and get a least-common-denominator spec for ::foo (e.g. (s/keys :req [(or ::bar ::baz)])
or (s/keys :req [::bar ::baz])
or you have to assert your different requirements on the spec for the outer map using predicates that look inside ::foo
(s/def ::foo-map-with-bar (s/and (s/keys :req [::foo]) #(-> % ::foo (contains? ::bar)))
spec has ironclad devotion to the principle that the keyword of an item in a map is its spec
there are a few ways to do this, but in the end you must do it in such a way that ::foo
has only one spec
(another possibility is making it a multispec and putting something on ::foo's map itself that determines if ::bar or ::baz is required)
So even if I spec (s/def ::foo-with-bar (s/keys :req-un [::bar] ::opt-un [::baz])
and (s/def ::foo-with-baz (s/keys :req-un [::baz] ::opt-un [::bar]))
and use the appropriate one in the given context, that will not work?
Sorry, found it myself: https://clojure.org/guides/spec#_multi_spec
if you give your two foo uses different keywords your outer spec is just (s/keys :req [::foo-with-bar])
or (s/keys :req [::foo-with-baz])
and everything is fine
the problem is you want those two different specs, BUT ALSO as the value of a map whose key is ::foo
The ::foo-with-* examples were broken. Should look like this:
(ns common)
(s/def ::bar int?)
(s/def ::baz int?)
(ns with-bar :require [common])
(s/def ::map-with-bar (s/keys :req-un [::foo]))
(s/def ::foo (s/keys :req-un [::common/bar] ::opt-un [::common/baz]))
(s/valid? ::map-with-bar {:foo {:bar 1}})
(ns with-baz :require [common])
(s/def ::map-with-baz (s/keys :req-un [::foo]))
(s/def ::foo (s/keys :req-un [::common/baz] ::opt-un [::common/bar]))
(s/valid? ::map-with-baz {:foo {:baz 2}})
OK, seems they strongly disagree with me: > Defining specifications of every subset/union/intersection, and then redundantly stating the semantic of each key is both an antipattern and unworkable in the most dynamic cases.
> unnamespacing the key is another possible workaround I thought I read somewhere that specs are forbidden from having non-namespaced keys.
(also you don't need to introduce ns forms when specing; maybe you are just doing to save typing but it isn't strictly necessary)
anyway, to deal with this exact problem you have I wrote a spec called keys+ which is a compromise: in a particular map, a particular key can get an additional (not replacement) spec to validate against and to use for generators
and contextually in certain s/keys specs, say it must conform to ::foo-with-bar or ::foo-with-baz also
I was dealing with data whose shape I could not control, and the alternative was renaming keys everywhere
but you can see from the gnarliness of this code that spec really doesn't even want this to be allowed
hi i’m working on an fspec for a macro. The macro itself, takes an argument that will become a spec basically something like (s/def
spec-key spec-pred)` in the macro body. Trying to come up with a valid spec for :spec-pred
since it could be a function or say (s/or … etc. etc.)
. So far I have this (s/or :fn fn? :spec s/spec?)
Specifically the patch in there
having a peculiar issue . I’m working on a spec for a variadic function, the ‘rest’ should be members of a set. In my case the set members are maps, and the test always fails. replacing them with say keywords works fine.
(defmacro test-mac
[one & many]
`[~one ~@many])
... works fine for (test-mac "a" :a), etc
(def base-set #{:a :b})
(s/fdef test-mac
:args (s/cat :one string? :many (s/* base-set)))
... fails for (test-mac "a" test-a)
(def test-a {:a :a})
(def test-b {:b :b})
(def test-set #{test-a test-b})
(s/fdef test-mac
:args (s/cat :one string? :many (s/* test-set)))
the problem is spec can't see what the value of test-a
is during expansion: https://clojure.org/guides/spec#_macros
could maybe move some functionality out of the macro into a function (and spec that), or reconsider whether you need a macro at all