This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-11-07
Channels
- # bangalore-clj (1)
- # beginners (255)
- # boot (29)
- # cider (16)
- # cljs-dev (13)
- # cljsrn (6)
- # clojure (200)
- # clojure-berlin (1)
- # clojure-dev (13)
- # clojure-dusseldorf (6)
- # clojure-greece (1)
- # clojure-india (1)
- # clojure-italy (1)
- # clojure-russia (33)
- # clojure-spec (28)
- # clojure-uk (27)
- # clojurescript (47)
- # cursive (32)
- # data-science (3)
- # datascript (1)
- # datomic (40)
- # emacs (39)
- # events (4)
- # fulcro (55)
- # graphql (16)
- # immutant (2)
- # luminus (2)
- # lumo (5)
- # off-topic (142)
- # onyx (50)
- # portkey (1)
- # re-frame (45)
- # reagent (80)
- # remote-jobs (2)
- # ring-swagger (3)
- # rum (9)
- # schema (3)
- # shadow-cljs (184)
- # spacemacs (3)
- # test-check (4)
- # unrepl (2)
- # yada (5)
map-like? I have an application and I use maps a lot. Added spec, most of them using s/keys. In some cases I want to apply operations to the maps like filter. But this breaks the specs, since it isn't a map any more, but a sequence of pairs. The simple solution is of course to convert the result of filter back to a map, but is there a better way? Here is a small sample
(s/def ::test-id int?)
(s/def ::test-data string?)
(s/def ::test1
(s/keys :req-un [::test-id ::test-data]))
(s/fdef spec-test
:args (s/cat :m ::test1)
:ret int?)
(defn spec-test
[m]
(count m))
(def test1-sample {:test-id 1 :test-data "hello"})
(defn works
[]
(spec-test test1-sample))
(defn works-not-which-is-ok
[]
(spec-test (dissoc test1-sample :test-data)))
(defn works-not-which-is-not-ok
[]
(spec-test (filter (fn [_] true) test1-sample)))
You can spec them as s/coll-of an s/tuple of key value pairs. That’s not great if you are still relying on attribute keys. I guess you could also use s/keys* on the kv tuple.
If I understand it correctly, s/keys* wants the structure [:a 1 :b 2]
, but I have [[:a 1][:b 2]]
Yeah, you’d want coll-of keys*
What is the best way to spec a map which has a single required key (s/keys :req-un [::id])
(s/def ::id int?)
where every other (optional) map key is a string?
with a string?
value? Such that (s/valid ::spec {:id 123}) ;; => true
, (s/valid ::spec {"foo" "bar" :id 123}) ;; => true
, (s/valid ::spec {"foo" "bar"}) ;; => false
here’s a really naive way to do it:
(s/def ::my-map
(s/and (s/keys :req-un [::id])
#(every? (fn [[k v]]
(or (= :id k)
(and (string? k) (string? v))))
%)))
maybe there’s a better way, but I’m not sure how you’d combine keys
+ map-of
specs like thisThese are sometimes called hybrid maps - I have a blog about the spec for destructuring which covers the techniques for handling them. http://blog.cognitect.com/blog/2017/1/3/spec-destructuring
taylor: yes I had something similar to your first, but the use of and
& or
in a single monolothic predicate bothered me, as it kills error message granularity further down the tree.
I think as you’ve discovered the use of every
and or
looks to be how to do it 🙂
Thanks @U064X3EF3 for the pro tips
How to write spec for “string of integer”? I want to create a generator of "string of integer" which should only generate valid string of integer (value within Integer/MIN_VALUE
and Integer/MAX_VALUE
).
you could write a predicate function int-str?
that returns true/false if the given string can be parsed as an integer, then it’s trivial to use that predicate as a spec
The generator would be (gen/fmap str an-appropriate-int-generator)
Thanks @U3DAE8HMG and @U0GN0S72R for sharing your ideas. Now I am able to do it as follows and it will also help in writing fdefs
.
(defn- in-integer? [x]
(and (>= x Integer/MIN_VALUE) (<= x Integer/MAX_VALUE))) (s/def ::in-integer?
(s/and number? in-integer?)) (s/def ::str-long?
(s/spec string?
:gen #(gen'/fmap str (s/gen ::in-long?))))
Quick Q about double-in
-- if I specify :min 0.0
and :max 1.0
, does that automatically exclude NaN and infinity? Or do I also need to specify :NaN? false :infinity? false
?
looking at https://github.com/clojure/clojure/blob/d920ada9fab7e9b8342d28d8295a600a814c1d8a/src/clj/clojure/spec.clj#L1630 it doesn’t look like specifying min/max has any effect on NaN/infinity (and they both default to true)
so I guess it implicitly excludes NaN/infinity by virtue of those not passing the range comparator checks?
Thanks @U3DAE8HMG That's sort of what I intuitively expected to happen but I wasn't sure how "special" NaN was... I guess that begs the question of what do you do if you want 0.0 .. 1.0
or NaN
? I suppose you have to :or
two specs together... but what would that second spec look like, i.e., how would you allow only NaN
or a range?
That allows any double, it seems.