# gen/for macro for alternate combinator syntax

I think the syntax of {{clojure.core/for}} would be a good fit for test.check's combinators. For example:

(defn gen-even-subset
"Returns a generator that generates an even-cardinality
subset of the given elements"
[elements]
(gen/for [bools (apply gen/tuple (repeat (count elements) gen/boolean))
:let [true-count (->> bools (filter identity) (count))]
:when (even? true-count)]
(->> (map list bools elements)
(filter first)
(map second)
(set))))

This combines the capabilities of {{fmap}}, {{bind}}, and {{such-that}} into a familiar syntax.

One downside here is the temptation to use multiple clauses for independent generators, resulting in a use of {{gen/bind}} when {{gen/tuple}} would be simpler and presumably shrink easier. An approach to this is an additional supported clause, perhaps called {{:parallel}}, that uses the syntax of {{:let}} to provide the functionality of {{gen/tuple}}:

(gen/for [:parallel [n1 gen/nat
n2 gen/nat]
:let [sum (+ n1 n2)]]
{:nums [n1 n2] :sum sum})

Compared to {{gen/tuple}}, this has the advantage of placing generators syntactically next to names, rather than segregating the generators from the names.

The {{:parallel}} feature has not been added to the current patches.

by

I think there might be some design ambiguity around the meaning of {{:when}}. In particular, in the following contrived example:

(for [n nat
v (vec (return n))
:let [sum (reduce + v)]
:when (pos? sum)]
v)

In my default design this can hang, for the same reason that this code can hang:

(bind nat
(fn [n]
(such-that
(fn [v] (pos? (reduce + v)))
(vector (return n)))))

But it could just as well have been written:

(such-that
(fn [v] (pos? (reduce + v)))
(bind nat (fn [n] (vector (return n)))))

So the issue is whether a {{:when}} filter is applied to just the previous generator or to all of the previous generators. I have some hazy notion that the latter would be less efficient in some cases, but I'm not sure what. So I think our options are:

# Decide to always do it one way or the other
# Provide a third keyword ({{:when-all}}?) with different behavior
# Don't write this macro at all because it's too difficult to understand

My gut is to do option 1 and just apply :when to the previous generator.
by

Attached my initial draft. The implementation took a lot more thought than I expected, and is a bit subtle, so I included some inline comments explaining the structure of the macro.

by

Attached TCHECK-15-p1.patch, updated to apply to the current master.

by

Attached TCHECK-15-p2.patch which adds a note to the docstring about independent clauses, shrinking, and tuple.

by

Attached TCHECK-15-p3.patch which fixes one bug and one redundancy in namespace aliases.

by

Attached TCHECK-15-p4.patch which fixes a bug with destructuring (and adds a regression test).

by

Also might be helpful to note that I've put this in my test.check utility library for now: https://github.com/fredericksgary/test.chuck#for.

by

I wonder if it'd be possible to avoid :parallel by analyzing the code and checking whether the bindings can be run in parallel?