This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-01-13
Channels
- # announcements (2)
- # beginners (29)
- # boot (122)
- # cider (9)
- # clara (6)
- # cljs-dev (9)
- # clojure (27)
- # clojure-art (3)
- # clojure-dusseldorf (5)
- # clojure-europe (1)
- # clojure-india (2)
- # clojure-losangeles (1)
- # clojure-spec (29)
- # clojure-uk (2)
- # clojurescript (56)
- # cursive (11)
- # datomic (9)
- # fulcro (8)
- # liberator (1)
- # off-topic (2)
- # other-lisps (1)
- # quil (37)
- # re-frame (2)
- # reitit (1)
- # ring (4)
- # test-check (4)
- # tools-deps (3)
I have a spec of map-entry and seqable-of-map-entry. Sometimes I get this exception while generating values:
(require '[clojure.spec.gen.alpha :as gen])
(require '[clojure.spec.alpha :as s])
(s/def ::map-entry
(s/with-gen map-entry?
(fn []
(gen/fmap first
(s/gen (s/and map? seq))))))
(s/def ::seqable-of-map-entry
(s/coll-of ::map-entry :kind seqable?))
(gen/sample (s/gen ::seqable-of-map-entry))
Error printing return value (ClassCastException) at clojure.core/conj (core.clj:82).
java.lang.String cannot be cast to clojure.lang.IPersistentCollection
I can imagine it tries to build a seqable, so it starts with an empty string, and then it tries to conj map-entries to it:
(conj "" (first {:a 1}))
for now I can use this workaround:
(s/def ::seqable-of-map-entry
(s/with-gen (s/coll-of ::map-entry :kind seqable?)
(fn []
(s/gen (s/coll-of ::map-entry :kind list?)))))
coll-of always gens a collection, never a lazy seq
Yes, I meant, if I add the into, it would generate correctly but it would realize lazy seqs
maybe something like this? (doesn’t work yet)
(s/def ::seqable-of-map-entry
(s/coll-of ::map-entry :kind (s/with-gen seqable?
#(s/gen vector?))))
so kind must be a predicate and cannot be a spec, but it must also generate. in other words, you can only use pre-defined predicates?
this seems to work:
(defn seqable-of
"Prevents generating strings and therefore Exceptions during generation"
[elt-spec]
(s/with-gen (s/coll-of elt-spec :kind seqable?)
#(s/gen (s/coll-of elt-spec :kind vector?))))
:kind seqable? does not make sense
coll-of always gens a collection
It’s a collection spec
Maybe just coll-of would work, since
(coll? (seq {:a 1 :b -1 :c 1 :d -1}))
(coll? (filter (comp pos? val) {:a 1 :b -1 :c 1 :d -1}))
are both truenope, it really should be a seqable, since (java.util.HashMap. {:a 1})
is also supposed to work.
that’s it. every also only checks a maximum number of elts, so a lazy infinite seq would still be supported. thanks. 🦆
isn’t this a bit inconsistent, since nil puns as an empty sequence?
user=> (s/conform (s/every string?) '())
()
user=> (s/conform (s/every string?) nil)
:clojure.spec.alpha/invalid
user=> (s/conform (s/every string? :min-count 0) nil)
:clojure.spec.alpha/invalid
user=> (count nil)
0
(s/conform (s/every string? :kind seqable?) nil)
works though, but then I’m back into the same problem where I started:
user=> (gen/sample (s/gen (s/every string? :kind seqable?)))
Error printing return value (ClassCastException) at clojure.core/conj (core.clj:82).
java.lang.String cannot be cast to clojure.lang.IPersistentCollection
:kind seqable?
is just not right here. every
(like coll-of
) is a spec for collections (not seqables). you are using a broader predicate for :kind than the spec is intended for.
why is (s/nilable (s/every ::map-entry))
not the right fit for seqables? the implementation uses seq
on the input and then checks every element up to a limit. so if this is not it, what’s the alternative?
I’m saying seqable? does not make sense with coll-of/every because some seqables are not collections
The default is vector iirc
@alexmiller would this be OK?
(defn seqable-of [spec]
(s/with-gen (s/and seqable?
(s/or :empty empty?
:seq (s/and (s/conformer seq)
(s/every spec))))
#(s/gen (s/nilable (s/every spec :kind coll?)))))