Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

I'm tinkering away with spec and I don't understand part of the design. There doesn't seem to be a good mechanism to express "this value satisfies a series of specs simultaniously".

  • and from clojure.core isn't useful, because then s/explain will not dig in to figure out which specific spec a data structure failed to meet.

  • s/and is actually a series of transforms and the logical value depends on the order of the arguments. I don't want to deal with that risk. I include a silly example a bit like what I was bitten by

Start with:

(s/def :example/s1 vector?)
(s/def :example/s2 vector?)
(s/def :example/spec (s/and :example/s1 :example/s2))
(s/valid? :example/spec [1 2 3]) ; => true

then change :example/s1 to:

(s/def :example/s1 (s/cat :a integer? :b integer? :c integer?))

now:

(s/valid? :example/spec [1 2 3]) ; => false

This put me in a position of trying to debug a spec which was a horrible experience; I don't want to be in that situation again. It isn't obvious to me how I would even start if the spec was more baroque and so I'd rather avoid functions that can transform/conform the data mid way through completely unless that is clearly required.

  • s/every seems fundamentally unsafe for general use - despite the name it doesn't check that a spec is true for every entry. I have functions that extend long vectors with new entries and a spec based on s/every will specifically not check the values that are most likely to contain bugs.

I'm in a position where I feel pressure to re-implement s/every (to be comprehensive) or s/and (to be validating but non-conforming) - but this is a bit confusing to me because I had just assumed what I wanted would be the common use case. Have I missed something? Why didn't a non-conforming variant of s/and get included in spec?

1 Answer

0 votes
by

Answering the question in the title - s/valid? is for checking if a value matches a spec. s/conform tells you how a value matches a spec (by showing you which alternative was taken and labeling parts of specs with components.

Re s/and, this was a point of long debate about whether to include flowing s/and or nonflowing. There are good use cases for both (and the generative side plays into this too). In the wip spec2, we have included a nonflowing and, currently called s/and- (but that name is tentative - might end up being called s/union or something else).

Re s/every, sounds like you want s/coll-of which conforms all elements. Again, both cases are useful (don’t want to exhaustively check an infinite seq!).

Regarding the specific case of ensuring a vector with a particular syntax (via cat), this is a somewhat common case in macros and is basically impossible to get out the current pieces and make the combination of conform, gen, explain, and unform do the right thing. As you found, you want nonflowing and! But we have provided this composite in spec2 as s/catv (like cat, but constrain to vectors).

https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha#nonflowing-sand--new

by
Thanks! And sorry, the question drifted in draft.
...