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
johanatan 2020-08-10T02:44:06.086500Z

@seancorfield i'm not sure how to force s/check to use my conformer but yes that would seem to work if it were possible to do so.

johanatan 2020-08-10T02:45:40.087200Z

i wonder if there is a "conformer registry" i could write into ?

seancorfield 2020-08-10T02:48:04.087900Z

No, I mean directly in your Spec. So it always runs when a Spec is conformed.

johanatan 2020-08-10T02:54:37.090500Z

suppose i have a spec defined like so:

(s/def ::an-entity
  (s/with-gen
    ::a-base-entity
    #(gen/let [...] (with-meta a-base-entity-conforming-structure {:some :metadata}))))
are you suggesting to use s/and to weave in a conformer like so: (s/and (s/conformer ...) ::a-base-entity) ?? or something else?

johanatan 2020-08-10T02:55:02.090700Z

@seancorfield ^

johanatan 2020-08-10T02:55:41.091Z

the base entity in this case looks like this:

(s/def ::re-frame-event
  (s/and vector?
         (s/cat :event-name qualified-keyword?
                :params (s/* any?))))

johanatan 2020-08-10T02:56:35.091600Z

actually i can also just share the derived event:

(s/def ::callback-func-event
  (s/with-gen
    ::re-frame-event
    #(gen/let [params (s/gen (s/coll-of any?))
               triggers-failure? (gen/frequency [[1 (gen/return true)] [9 (gen/return false)]])
               event-name (s/gen qualified-keyword?)]
       (with-meta
         (concatv [event-name] params)
         {:triggers-failure? triggers-failure?}))))

seancorfield 2020-08-10T02:57:49.092200Z

Put s/conformer in the second/last slot of s/and

seancorfield 2020-08-10T02:58:25.092900Z

That allows you to modify the (conformed) value as part of the conform process without affecting the validation.

johanatan 2020-08-10T02:59:13.093200Z

ah, ok

seancorfield 2020-08-10T02:59:54.093900Z

(s/and ... (s/conformer #(with-meta % {:what :ever}))) -- untested but that's what I had in mind.

johanatan 2020-08-10T03:01:28.094200Z

hmm, ok. let me try that

johanatan 2020-08-10T03:02:23.094700Z

the problem with that is that the meta depends on a generated value

johanatan 2020-08-10T03:02:37.095Z

and i need to somehow "attach" that value to the generated structure

johanatan 2020-08-10T03:02:50.095300Z

while allowing it to survive the first "conform" in the s/and

johanatan 2020-08-10T03:03:41.095900Z

i.e., the following doesn't work because ::re-frame-event has already stripped away the meta and destructured into labeled data

johanatan 2020-08-10T03:03:43.096200Z

(s/def ::callback-func-event
  (s/with-gen
    (s/and ::re-frame-event (s/conformer identity))
    #(gen/let [params (s/gen (s/coll-of any?))
               triggers-failure? (gen/frequency [[1 (gen/return true)] [9 (gen/return false)]])
               event-name (s/gen qualified-keyword?)]
       (with-meta
         (concatv [event-name] params)
         {:triggers-failure? triggers-failure?}))))

johanatan 2020-08-10T03:04:40.096700Z

i could "cheat" and stuff it into the last "param" and allow my conform to strip it out of there and put it in the meta

johanatan 2020-08-10T03:04:52.097Z

but that kind of violates a sense of purity 🙂

johanatan 2020-08-10T03:05:50.097800Z

and would also only work for some specific subset of cases; i.e., we're lucky in the sense that params is an s/* of s/any which could accommodate the data

johanatan 2020-08-10T03:06:50.098600Z

perhaps i just make the single conformer the "spec" and forget about using s/and ?

johanatan 2020-08-10T03:07:03.099100Z

then do the base conform within that conform

seancorfield 2020-08-10T03:07:15.099400Z

True, you could have a completely custom "predicate" that conforms as a spec...

johanatan 2020-08-10T03:07:45.099900Z

yea, i think i'll go that route. probably the path of least resistance at this point

johanatan 2020-08-10T03:13:01.100100Z

this works:

(defn metadata-preserving [spec]
  (s/conformer
   (fn [v]
     (let [m (meta v)
           res (s/conform spec v)]
       (if (= res ::s/invalid)
         res
         (with-meta res m))))))

johanatan 2020-08-10T03:13:59.101200Z

used like so:

(s/def ::a-spec
  (with-gen
     (metadata-preserving ::a-base-spec)
     #(with-meta ( ... generation here ) {:some :meta})))

johanatan 2020-08-10T03:15:24.101500Z

thanks!

1
seancorfield 2020-08-10T04:10:52.102Z

Nice!