Fork me on GitHub
#clojure-spec
<
2017-12-18
>
donaldball20:12:31

I’m struggling to figure out a good way to express a spec category that has come up before, but I can’t recall what the best recommendations are any longer. The value is a map with namespace keyword keys, two of which each require values in their own sets, but further the pair of values must occur its own set. For example, :pet/type could be #{:cat :dog :snek} and :pet/fur? could be boolean?, but {:pet/type :snek :pet/fur? true} is not allowed. This is trivial with a predicate spec but then I lose any hope that s/explain can identify the paths to the keys contributing to the violation.

misha21:12:56

@donaldball yeah, doable with multispec, but verbose. Especially if you'd overload second value based on type of the first (different value sets for :pet/fur? depending on :pet/type

misha21:12:52

plus, you'd need to supply custom generators to avoid "could not satisfy predicate after # tries", when amount of valid value invariants is noticeably smaller than count of combinations in cartesian product of both value sets.

donaldball21:12:18

I thought about multispec but I wasn’t even sure how I could tell spec “hey use a smaller spec for this namespaced key in s/keys this time” and couldn’t figure out a good way to express it using s/map-of 🙂

misha21:12:49

speaking of "verbose", how you, ladies and gentlemen, organize your specs, when there is fair amount of "intermediate" specs, which are not exactly useful in and of themselves, but are used to build larger composite specs. E.g. when "leaf" specs are "useful", and "root" spec is "useful" (for eyeballing), but "branch" specs in between are sort of just glue and are noisy. "Just suck it up" – is an acceptable answer, I guess opieop

misha21:12:27

@donaldball you can "alias" specs

misha21:12:32

(s/def :cat/color #{:grey :white})
(s/def :dog/color #{:black :white})
(s/def :pet/color :dog/color)

misha21:12:24

the thing is, when you want to overload a :pet/foo key value based on :pet/type value, you can get away with this in spec by using unqualified keywords in your entity maps {:foo ..., :type}. but if you want to overload value of a namespaced key – it will be a mess to spec it, if possible at all

misha21:12:46

@donaldball you still have an option to return these from multispec

(s/keys :req [:pet/type :cat/fur?])  ;; for type=cat
(s/keys :req [:pet/type :snake/fur?]) ;; for type=snake
where
(s/def :pet/fur? boolean?)
(s/def :cat/fur? :pet/fur?)
(s/def :snake/fur? false?)

donaldball22:12:47

Yeah, but all these options seem to require adjusting the form of my inputs. While the input form is not necessarily desirable, I’d like to try to spec is it is, not as I have transformed it to be.