Fork me on GitHub
#clojure-spec
<
2018-01-22
>
witek09:01:27

I want to spec a command vector, given (s/def ::command-name keyword?). Now the command-vector needs to be a vector where the first element must be the ::command-name. All other elements are optional arguments. Any suggestions? Thank you.

taylor13:01:06

(s/def ::command-name keyword?)
(s/def ::command (s/cat :name ::command-name
                        :args (s/* any?)))
(s/conform ::command [:foo "bar"])

taylor13:01:03

if you really need the input to be a vector then

(s/def ::command
  (s/and
    vector?
    (s/cat :name ::command-name
           :args (s/* any?))))

jakob09:01:15

I have a datastructure that looks something like this:

(def my-invoice-address
  {
   :use-delivery-address false
   :street "asdf"
   :zip "123"})
If :use-delivery-address is true, :street and :zip should not be there. If :use-delivery-address is false, :street and :zip should be there I find it really difficult to write a spec for this. Anyone who have any ideas?

stathissideris09:01:18

@karl.jakob.lind I guess :use-delivery-address is :req, the others are :opt and then you wrap your keys with an s/and to enforce the rest of the logic that you described

jakob09:01:59

Well, the others are required if :use-delivery-address is true. So it's not that simple

stathissideris09:01:48

yeah, but I’m proposing that this is enforced by the extra predicate that you will pass to s/and

jakob09:01:54

hm.. something like this?

(s/def ::invoice-address
  (s/keys
   :req-un [::use-delivery-address]
   :opt-un [::street ::zip]))
where do the s/and belong ?

stathissideris09:01:58

something like this (I’m sure there’s a more elegant way to do it):

stathissideris09:01:01

(s/def ::invoice-address
  (s/and
   (s/keys
    :req-un [::use-delivery-address]
    :opt-un [::street ::zip])
   (fn [{:keys [use-deliver-address street zip]}]
     (or (and (not use-deliver-address)
              (not street)
              (not zip))
         (and use-deliver-address
              street
              zip)))))

jakob09:01:23

That's at least more elegant than my attempts 🙂 Thanks! will use it!

Jakub Holý (HolyJak)09:01:30

Perhaps use https://clojure.org/guides/spec#_multi_spec ?

(defmulti address-type :address/use-deliver-address)
(defmethod address-type true [_]
  (s/keys :req []))
(defmethod address-type false [_]
  (s/keys :req [ :address/street ...]))
@karl.jakob.lind

jakob09:01:13

good suggestion @karl.jakob.lind. will read about that!

carkh16:01:24

let's say i want to write a spec for a string that should be splitted in two, imagine i have a split function that can be called with that string and returns a pair of strings... i have a ::check-whole-thing spec and also a ::check-part spec ...how would i write that ::check-whole-thing part ?

carkh16:01:29

there are numerous examples, but these all work on clojure data structures

carkh16:01:28

i think what i'm asking here is how to transform the data being specced before speccing it further

Alex Miller (Clojure team)17:01:54

have you read the spec guide? https://clojure.org/guides/spec If not, maybe take a spin through that first and then refine the question.

carkh17:01:04

i did ... actually found a related question to which you responded on the forum ... the conformer function looks like it fits the use case

Alex Miller (Clojure team)17:01:35

that does not seem like a use case where conformers should be used to me

Alex Miller (Clojure team)17:01:19

I’m confused by several aspects of your question so not sure how best to answer it

carkh17:01:12

i'm conforming a string, i want to have it transformed into a vector of strings before further conforming

carkh17:01:23

could be any kind of transformation really

Alex Miller (Clojure team)17:01:25

well, I would recommend not doing that

Alex Miller (Clojure team)17:01:32

do that in code explicitly

Alex Miller (Clojure team)17:01:37

don’t use spec for it

carkh17:01:59

but semantically i'm really validating the string there

carkh17:01:08

which is what spec is for isn't it ?

Alex Miller (Clojure team)17:01:40

I think you are better off if you spec the inputs and outputs of the transformation

Alex Miller (Clojure team)17:01:47

and explicitly invoke the transformation

Alex Miller (Clojure team)17:01:18

and don’t use spec to actually do the transformation part

carkh17:01:24

the thing is that i'm not really interested in that transformation at all, i'm only validating it to pass the whole thing merilly along

Alex Miller (Clojure team)17:01:44

if you have to do that transformation in able to pass it along, then it seems like you are interested in it after all

carkh17:01:47

let's imagine a use case for one of those html dsls ... they have this way of specifying class attributes like so [:div.app-container ...] ...that's not what i'm doing at all but that's a good example... are you saying that spec wouldn't fit to do the "destructuring" of that keyword ?

carkh17:01:08

or maybe to destructure an udp packet or you know go inside a data structure and destructure it for me

carkh17:01:12

that last one was a bad example =)

carkh17:01:07

what's the issue here ? the fact that one could not rebuild the data structure from the conformed result ?

ghadi17:01:40

to rephrase slightly, don't conflate spec/validation with transformation in one phase

ghadi17:01:47

you'll be perpetually unhappy

ghadi17:01:11

do your validation, do transformation, then do validation again if you want

ghadi17:01:43

there are other gotcha with conform to look into

ghadi17:01:58

like it forces conformance on all consumers of the spec

ghadi17:01:02

which you don't want

carkh17:01:51

ok i need to think a little bit on this

carkh17:01:59

thansk for your time the both of you

ghadi17:01:31

no problem. Lots of other validation libraries provide "mutative validation".... conflating transformation into its API

ghadi17:01:36

really impedes reuse

ghadi17:01:03

Typical example is 'validate this string into a UUID'

carkh17:01:17

that's a good example

carkh17:01:28

but now i first need to conform a big data structure, then walk it and validate parts of it

carkh17:01:41

that's a lot more code ><

carkh17:01:57

anyways i'll think on it, thanks again !

onionpancakes20:01:35

I'm trying to spec polymorphic maps where a particular key is used for dispatch. Is there a way to spec the dispatch key to specify the dispatch value?

(s/def ::foo
  (s/keys :req [:data/type :foo/stuff]))

(s/def ::bar
  (s/keys :req [:data/type :bar/stuff]))

e.g. ::foo's :data/type should have a value of :foo, and ::bar's :data/type have a value of :bar

onionpancakes21:01:37

Thanks, I'll look into it. That retag argument looks interesting.