Fork me on GitHub
#testing
<
2020-06-28
>
Vincent Cantin06:06:43

is it possible to reuse clojure.test.check.generators/such-that so that it returns a value from an alternative generator if it runs out of tries?

Vincent Cantin07:06:04

I would like to implement a function (preferably-such-that pred gen opts) but don’t know how to do it without accessing the internals of clojure.test.check .

Vincent Cantin07:06:38

any help is welcome

Vincent Cantin08:06:35

Here is my working function. If someone knows a way to avoid tapping into the private function make-gen, please let me know. cc @gfredericks

(defn preferably-such-that
  "A generator that tries to generate values satisfying a given predicate,
   but won't throw an tantrum if it can't."
  [pred gen max-tries]
  (#'gen/make-gen (fn [rng size]
                    (loop [tries-left max-tries
                           rng rng
                           size size]
                      (if (zero? tries-left)
                        (gen/call-gen gen rng size)
                        (let [[r1 r2] (random/split rng)
                              value (gen/call-gen gen r1 size)]
                          (if (pred (rose/root value))
                            (rose/filter pred value)
                            (recur (dec tries-left) r2 (inc size)))))))))

gfredericks13:06:31

off the top of my head I don't think you can do what you're describing using the public API

gfredericks13:06:10

The intention is that if you use such-that, you ensure that there's a (hopefully high) positive lower bound on the probability of the generator succeeding, such that (ha!) there's some reasonable value of max-tries that makes failure unlikely before the universe ends

Vincent Cantin14:06:36

I have another problem, I want a generator of sets which take as input a list of element generators. Formulated differently, I want a generator of tuple where the generated elements can be placed into a set (i.e. they should be distincts). @gfredericks Do you know a simple way to do that with the public API? That’s for my budget-based generator of recursive structures, each elements in my set is given a different part of the parent’s budget.

gfredericks14:06:30

I don't quite understand what you mean, but presumably you're aware of the *-distinct generators, so you're describing something those can't do?

Vincent Cantin14:06:26

I saw them, but they all take 1 generator as input, and I have n generators, 1 for each element of my set, each of them generating elements on a different budget.

gfredericks14:06:22

so you could call this tuple-distinct, right?

Vincent Cantin14:06:31

I did not know that it existed, give me a few minutes to take a look …

gfredericks14:06:20

I'm just saying that if it did exist, that's what you're describing

Vincent Cantin14:06:23

yes, I could call it tuple-distinct 🙂

gfredericks14:06:51

I think it's a useful point because tuple-distinct seems like a simple concept and fits well with the existing distinct generators, compared to some more complicated & idiosyncratic thing that you might have been describing

gfredericks14:06:08

so I can more easily imagine tuple-distinct being in test.check

Vincent Cantin14:06:27

I will implement it locally in my library for now, but if it makes its way to test.check, I will be happy to link to it.

👍 3
gfredericks14:06:41

You could create a jira ticket suggesting it if you want; also you probably figured out that you can implement a crude version of this in userland using tuple and such-that

gfredericks14:06:53

I'm actually not sure what a fancier version would do exactly

gfredericks14:06:05

Would it only generate things in order?

gfredericks14:06:30

by which I mean, if it fails to get a distinct element for the last generator, will it give up, or do something fancier?

Vincent Cantin14:06:19

I was thinking about doing something fancier, but that’s better done in userland rather than in test.check: the retry behavior highly depends on what the user wants to do with his data.

Vincent Cantin14:06:27

as for preferably-such-that , I think that such-as could be augmented with an optional fallback function that returns a generator to use when the tries-left reaches zero.

gfredericks14:06:44

I agree that this is a coherent thing to ask for; it feels less obviously useful, but it's not too intrusive to the API not my call anymore; in any case I guess I'd say I'm on the fence about it

Vincent Cantin14:06:32

in fact, the extension to such-as would be super-useful for tuple-distinct , as the user’s code could have a change to reduce the number of elements if it fails to create distinct elements.

Vincent Cantin14:06:15

tuple-distinct could be then easily made in userland.

gfredericks14:06:11

reduce the number of elements? Like return a smaller tuple?

gfredericks14:06:25

that's an odd definition of a tuple

Vincent Cantin14:06:46

In my use case, I want my data to be a set, in the end.

gfredericks14:06:02

in my mind a tuple is inflexible w.r.t. number of elements

Vincent Cantin14:06:33

My usecase is somehow experimental, I will see later if my idea of test.check extension still holds. Thanks again for your help.

👍 3