clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
johnjelinek 2020-10-12T23:27:12.080800Z

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?)

johnjelinek 2020-10-12T23:28:02.081500Z

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

johnjelinek 2020-10-12T23:28:22.081900Z

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

alexmiller 2020-10-12T23:28:38.082400Z

Can you give an example of valid data?

alexmiller 2020-10-12T23:29:15.083200Z

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

alexmiller 2020-10-12T23:29:59.083900Z

Are you talking about in a map?

johnjelinek 2020-10-12T23:30:01.084200Z

ya

alexmiller 2020-10-12T23:30:33.085300Z

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

johnjelinek 2020-10-12T23:31:50.086900Z

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)

johnjelinek 2020-10-12T23:33:45.087800Z

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

alexmiller 2020-10-12T23:37:37.091Z

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

alexmiller 2020-10-12T23:38:27.091800Z

Just spec ::total as an int? or whatever

johnjelinek 2020-10-12T23:38:30.092Z

oh!

johnjelinek 2020-10-12T23:38:34.092300Z

ok great!

alexmiller 2020-10-12T23:38:44.092700Z

The attribute by itself does not have this constraint

alexmiller 2020-10-12T23:39:07.093400Z

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

johnjelinek 2020-10-12T23:40:05.093600Z

I'm not sure I grok that yet

alexmiller 2020-10-12T23:40:50.094300Z

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

johnjelinek 2020-10-12T23:42:42.094700Z

ah, right

johnjelinek 2020-10-12T23:50:00.095200Z

(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})

johnjelinek 2020-10-12T23:50:12.095500Z

this spits out an java.lang.IndexOutOfBoundsException

johnjelinek 2020-10-12T23:54:36.095800Z

(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