Fork me on GitHub
#clojure-spec
<
2020-10-12
>
johnjelinek23:10:12

is there a way to make a spec validate a reference to another key's value? let's say you have a (s/coll double?) and you want to make a key where the predicate is something like (s/and double? #(= % (apply + ,,,)) where the ,,, is that (s/coll double?)

johnjelinek23:10:02

maybe your schema is like (s/schema [::double-list ::sum])

johnjelinek23:10:22

I'm prolly not doing a great job of explaining this, does it vaguely make sense?

Alex Miller (Clojure team)23:10:38

Can you give an example of valid data?

Alex Miller (Clojure team)23:10:15

The example you gave should basically work so I’m trying to make sure I understand what you mean

Alex Miller (Clojure team)23:10:59

Are you talking about in a map?

Alex Miller (Clojure team)23:10:33

You can s/and a predicate that validates whatever you want

johnjelinek23:10:50

ok, so let's say concretely, I've got something like: {::prices '(1 2 3 4) ::total 10} and so total's predicate has to match the collection of prices (except doubles instead of ints)

johnjelinek23:10:45

so, how would a (s/def ::total)'s predicates (apply + on ::prices?

Alex Miller (Clojure team)23:10:37

(s/def ::m (s/and (s/keys ...) (fn [{::keys [total prices]}] (= total (reduce + prices))))

Alex Miller (Clojure team)23:10:27

Just spec ::total as an int? or whatever

Alex Miller (Clojure team)23:10:44

The attribute by itself does not have this constraint

Alex Miller (Clojure team)23:10:07

It only has that constraint when combined with prices in a map

johnjelinek23:10:05

I'm not sure I grok that yet

Alex Miller (Clojure team)23:10:50

The total=prices is a property of the map, not of the total

johnjelinek23:10:00

(s/def ::total int?)
  (s/def ::prices (s/coll-of int?))
  (s/def ::m (s/and (s/keys :req [::total ::prices])
                    (fn [{::keys [total prices]}] (= total (reduce + prices)))))
  (s/valid? ::m {::prices '(1 2 3 4) ::total 10})

johnjelinek23:10:12

this spits out an java.lang.IndexOutOfBoundsException

johnjelinek23:10:36

(let [{::keys [total prices]} {::prices '(1 2 3 4) ::total 10}]
    (= total (reduce + prices))) ;=> true
((fn [{::keys [total prices]}]
     (= total (reduce + prices))) {::prices '(1 2 3 4) ::total 10}) ;=> true
(s/def ::m (fn [{::keys [total prices]}]
                 (= total (reduce + prices)))) ;=> java.lang.IndexOutOfBoundsException
(s/def ::m (s/and (s/keys :req [::total ::prices])
                    (fn [{::keys [total prices]}]
                      (= total (reduce + prices))))) ;=> :scratch/m
(s/valid? ::m {::prices '(1 2 3 4) ::total 10}) ;=> java.lang.IndexOutOfBoundsException