This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-30
Channels
- # announcements (4)
- # babashka (8)
- # beginners (124)
- # calva (13)
- # cider (10)
- # circleci (6)
- # clj-kondo (193)
- # cljdoc (1)
- # cljs-dev (4)
- # clojure (50)
- # clojure-europe (28)
- # clojure-serbia (1)
- # clojure-spec (22)
- # clojure-uk (30)
- # clojurescript (11)
- # clojureverse-ops (3)
- # community-development (1)
- # conjure (5)
- # cursive (1)
- # datomic (11)
- # depstar (1)
- # events (2)
- # fulcro (7)
- # graalvm (2)
- # graphql (10)
- # helix (43)
- # hyperfiddle (14)
- # introduce-yourself (6)
- # jobs (2)
- # jobs-discuss (14)
- # kaocha (4)
- # luminus (2)
- # malli (24)
- # meander (6)
- # off-topic (4)
- # pathom (1)
- # polylith (13)
- # re-frame (6)
- # releases (1)
- # remote-jobs (1)
- # sci (14)
- # shadow-cljs (209)
- # tools-deps (30)
- # xtdb (26)
If you are using cat
, is it possible to somehow spec on all remaining elements, instead of just the next element in the sequence?
With *
perhaps? (not quite sure what you're asking @isak)
I'm trying to do something like this: (s/cat :a ::number :b ::number :rest ::my-spec)
, where ::my-spec
should run on all remaining items in the sequence, not just the very next one. For example for the sequence [1 2 3 4], it should run on [3 4], not just 3.
Like so?
dev=> (s/valid? (s/cat :a number? :b number? :c (s/* string?)) [1 2])
true
dev=> (s/valid? (s/cat :a number? :b number? :c (s/* string?)) [1 2 3])
false
dev=> (s/valid? (s/cat :a number? :b number? :c (s/* string?)) [1 2 "foo"])
true
dev=> (s/valid? (s/cat :a number? :b number? :c (s/* string?)) [1 2 "foo" "bar"])
true
dev=> (s/conform (s/cat :a number? :b number? :c (s/* string?)) [1 2])
{:a 1, :b 2}
dev=> (s/conform (s/cat :a number? :b number? :c (s/* string?)) [1 2 3])
:clojure.spec.alpha/invalid
dev=> (s/conform (s/cat :a number? :b number? :c (s/* string?)) [1 2 "foo"])
{:a 1, :b 2, :c ["foo"]}
dev=> (s/conform (s/cat :a number? :b number? :c (s/* string?)) [1 2 "foo" "bar"])
{:a 1, :b 2, :c ["foo" "bar"]}
Yea like that, except I want to be able to run a spec on the whole collection that gets gathered
I don't understand.
Do you mean that you want ::my-spec
to be another s/cat
-based spec?
Well with (s/* string?
), it is saying what should be true about the individual elements, but what if I needed something to be true for the whole collection?
Use s/cat
there.
dev=> (s/valid? (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2])
false
dev=> (s/valid? (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 3])
false
dev=> (s/valid? (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 "foo"])
true
dev=> (s/valid? (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 "foo" "bar"])
true
dev=> (s/conform (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2])
:clojure.spec.alpha/invalid
dev=> (s/conform (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 3])
:clojure.spec.alpha/invalid
dev=> (s/conform (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 "foo"])
{:a 1, :b 2, :c {:d "foo"}}
dev=> (s/conform (s/cat :a number? :b number? :c (s/cat :d string? :e (s/? string?))) [1 2 "foo" "bar"])
{:a 1, :b 2, :c {:d "foo", :e "bar"}}
The sequence regex specs "unroll" or merge into one long sequence, so you can use them to describe subsequences inside another sequence.
I'm showing the Spec forms inline, but that Spec for :c
(or :rest
in your case) could be ::my-spec
.
Hm, but what if I don't have a condition about the individual elements? For example, if I needed to match a number, a number, then all the remaining elements have to be in a collection that is odd?
For example, [1 2 3 4 5] would pass, but not [1 2 3 4] (contrived example, but gets at what I'm trying to do)
(And I can only do a cat
on the first 2 elements)
I would try this but it doesn't really make sense:
(s/def ::odd-collection (s/and seqable? #(odd? (count %))))
(s/conform
(s/cat :a int? :b int? :rest (s/cat ::odd-collection ))
[1 2 3 4 5])
"a collection that is odd" -- what do you mean? Can you express that as a predicate?
Ok this works:
(s/def ::odd-collection (s/and seqable? #(odd? (count %))))
(s/conform
(s/cat :a int? :b int? :rest (s/& (s/* any?) ::odd-collection))
[1 2 3 4 5 ])
That was what I was about to suggest: s/&
is the "and" of sequence regexes!
dev=> (s/conform (s/cat :a number? :b number? :c (s/& (s/* number?) (comp odd? count))) [1 2 3 4])
:clojure.spec.alpha/invalid
dev=> (s/conform (s/cat :a number? :b number? :c (s/& (s/* number?) (comp odd? count))) [1 2 3])
{:a 1, :b 2, :c [3]}
dev=> (s/conform (s/cat :a number? :b number? :c (s/& (s/* number?) (comp odd? count))) [1 2 3 4 5])
{:a 1, :b 2, :c [3 4 5]}
Gotcha, thanks @seancorfield