Fork me on GitHub
#clojure-spec
<
2019-06-20
>
Ben Hammond07:06:03

I am calling generate on a complex data structure. Sometimes I see this error. I would normally troubleshoot this by commenting out parts of the spec to zone in on the problem; but I'm wondering if there is a smarter way to debug generators

Ben Hammond07:06:18

is there a direct way to dicover the specific sub-spec that has the problem?

Ben Hammond09:06:12

okay another question; I want a generator that has two modes of operation • when there is data available in the dynamc variable *existing-ids* then I want it to behave like (element *existing-ids)` • when *existing-ids* is empty then I want it to behave like (spec/gen string?) this must be a fairly common use case how has it been solved before?

Ben Hammond09:06:09

I'm thinking something like

(test.gen/bind
  (test.gen/return nil)
  (fn [_]
      (if (not-empty *existing-ids*)
        (test.gen/elements *existing-ids*)
        test.gen/string-alpha-numeric)))

Ben Hammond10:06:28

that doesn't seem a very smart solution though

Ben Hammond10:06:34

I think that's the best I can do, unless I hack directly into

(defn- bind-helper
or
(defn- make-gen
but that doesn't seem like a very good idea either

Ben Hammond10:06:07

(def ^:dynamic *existing-ids* nil)
=> #'dev/*existing-ids*
(def gg (test.gen/bind
          (test.gen/return nil)
          (fn [_] (if (not-empty *existing-ids*)
                    (test.gen/elements *existing-ids*)
                    test.gen/string-alphanumeric))))
=> #'dev/gg
(spec.gen/generate gg)
=> "9Dlv2SuFqT4Jb"
(binding [*existing-ids* [:this :that :tother]]
  (spec.gen/generate gg))
=> :that

Ben Hammond10:06:20

I suppose I could store the test.gen/elements in the *existing-ids* var

Ben Hammond10:06:05

(spec/with-gen takes a gen-fn, and I'd hope that I could use this to choose between generators, but of course it doesn't get called very often

Ben Hammond10:06:32

this might be better actually

(defn build-indirect-gen 
  [vargen] 
  (test.gen/bind
    (test.gen/return nil)
    (fn [_] (deref vargen))))

(def ^:dynamic *existingids-gen* test.gen/string-alphanumeric)
(def gg (build-indirect-gen #'*existingids-gen*))

(binding [*existingids-gen* (test.gen/elements [:this :that])]
  (test.gen/generate gg))
=> :that 

(test.gen/generate gg)
=> "Sjeu5IwBWbMUZl"

misha15:06:45

@ben.hammond afair, Gary touched on it debugging "Couldn't satisfy such-that predicate after 100 tries" here: https://www.youtube.com/watch?v=F4VZPxLZUdA

👍 4
misha15:06:18

this is the timestamp

Ben Hammond15:06:42

I've not seen that video. that's very interesting

Ben Hammond16:06:46

i'd not seen the size/scale explained properly before

Ben Hammond16:06:57

I'd assumed they represented mean/standard deviation

Alex Whitt18:06:54

I'm sure this has been asked and answered, but I can't find the answer... Why can I not do the following?

(s/def ::a any?)
(s/def ::thing (let [ks [::a]] (s/keys :req ks)))
That yields Don't know how to create Iseq from: clojure.lang.Symbol.

Alex Whitt18:06:51

I'm guessing that macros are the only way to dynamically define specs?

favila18:06:11

pretty much

favila18:06:50

I think spec2 has some improvements to this, but still isn't going to let you plop a let in there

favila18:06:21

I think spec2 will expose a public ast-like interface for programmatic spec creation/manipulation of specs as data

favila18:06:30

but the surface syntax will still be macros

Alex Whitt18:06:38

Hmm, alright. I'm interested in the spec2 feature you mentioned so I'll take a look. Thanks!

seancorfield19:06:27

Spec 2 has a macro layer and a function layer that implements the macros. You can programmatically construct pretty much any spec in Spec 2.

seancorfield19:06:26

(we have a branch of our code base at work that uses Spec 2 -- so we can track the sort of differences we'll need to make when we transition from Spec 1)

Alex Whitt13:06:58

Ah, that's exciting!