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?
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
.
any help is welcome
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)))))))))
off the top of my head I don't think you can do what you're describing using the public API
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
@gfredericks thanks
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.
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?
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.
so you could call this tuple-distinct
, right?
I did not know that it existed, give me a few minutes to take a look …
it doesn't
I'm just saying that if it did exist, that's what you're describing
yes, I could call it tuple-distinct
🙂
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
so I can more easily imagine tuple-distinct
being in test.check
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.
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
I'm actually not sure what a fancier version would do exactly
Would it only generate things in order?
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.
yes, in order.
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.
tuple-distinct
could be then easily made in userland.
by which I mean, if it fails to get a distinct element for the last generator, will it give up, or do something fancier?
reduce the number of elements? Like return a smaller tuple?
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.
that's an odd definition of a tuple
yes
In my use case, I want my data to be a set, in the end.
in my mind a tuple is inflexible w.r.t. number of elements
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
My usecase is somehow experimental, I will see later if my idea of test.check extension still holds. Thanks again for your help.