From 110eebaf15768d31d26be3fa52abc8fc25f8488f Mon Sep 17 00:00:00 2001 From: Carlo Zancanaro Date: Mon, 1 Jun 2015 16:40:53 +1000 Subject: Add some docs to the Generator interface --- .../java/au/id/zancanaro/javacheck/Generator.java | 89 +++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/main/java/au/id/zancanaro/javacheck/Generator.java b/src/main/java/au/id/zancanaro/javacheck/Generator.java index 2d2e556..4b9113c 100644 --- a/src/main/java/au/id/zancanaro/javacheck/Generator.java +++ b/src/main/java/au/id/zancanaro/javacheck/Generator.java @@ -4,13 +4,62 @@ import java.util.List; import java.util.Random; import java.util.function.Function; +/** + * Generators are a way of producing random objects and their associated shrink + * trees in a controlled and deterministic way. + * + * A generator must implement one method: {@link #generate(Random, int)}. The + * {@link RoseTree} it produces defines both the value to be returned, as well + * as the shrink tree for that value. + * + * Generators form a "monad", and hence have the {@link #map(Function)} and + * {@link #flatMap(Function)} methods for composition. The helper methods {@link + * #pure(Object)}, {@link #tuple(Generator[])} and {@link #list(int, Generator)} + * allow for composition at a lower level than the monadic actions.. + * + * @param The type generated by this generator. + */ public interface Generator { + /** + * Return a {@link RoseTree} containing a new random value of the required + * type, as well as its associated shrink tree. + * + * Generators also have an abstract notion of "size". Represented by an + * integer, generators should use this as a guide for how large an object to + * generate. There is no specific meaning for this value, so the precise + * interpretation will depend on the specifics of the generator. + * + * @param random The random source for generation purposes + * @param size An integer specifying how "big" a thing to produce + * @return The {@link RoseTree} specifying the generated thing and its + * shrink tree + */ RoseTree generate(Random random, int size); + /** + * A generator which simply generates the provided value. Does not shrink. + * + * @param value The value to generate + * @param The type of the generated value + * @return A {@link Generator} which generates the provided value + */ static Generator pure(T value) { return (random, size) -> RoseTree.pure(value); } + /** + * A generator which generates a {@link List}, with each element taken from + * its corresponding generator. (That is: return[i] = + * generators[i].generate(...).) + * + * Shrinking for this type involves attempting to shrink each subtree in + * turn, recursively. + * + * @param generators The generators to use for each term in the generated + * result + * @param The parameter type of the generated {@link List} + * @return A {@link Generator} returning a {@link List} + */ @SafeVarargs static Generator> tuple(Generator... generators) { return (random, size) -> { @@ -24,21 +73,57 @@ public interface Generator { }; } - static Generator> list(int count, Generator gen) { + /** + * A generator which generates a {@link List} of length count, with each + * element taken from the provided generator. + * + * Shrinking for this type involves attempting to remove terms and shrink + * each subtree in turn, recursively. (If the length must remain fixed then + * the {@link RoseTree} produced by this generator should be filtered with + * {@link RoseTree#filter(java.util.function.Predicate)}. + * + * @param count The length of the list to generate + * @param generator The generator to use for each term in the generated + * result + * @param The parameter type of the generated {@link List} + * @return A {@link Generator} returning a {@link List} + */ + static Generator> list(int count, Generator generator) { return (random, size) -> { @SuppressWarnings("unchecked") RoseTree[] result = (RoseTree[]) new RoseTree[count]; for (int i = 0; i < count; ++i) { - result[i] = gen.generate(random, size); + result[i] = generator.generate(random, size); } return RoseTree.shrink(Function.identity(), result); }; } + /** + * Transform the result of a generator by passing it through a function. + * + * Maps all values in the shrink of this through f, as well. + * + * @param f The transformation function + * @param The result of the transformation + * @return A new generator resulting from mapping f over this + */ default Generator map(Function f) { return (random, size) -> this.generate(random, size).map(f); } + /** + * Produce a new generator relying on the value generated by this generator + * + * Shrinking is a bit hard to predict under {@link #flatMap}, as it will + * first attempt to shrink this, resulting in the re-evaluation of action, + * and hence the re-generation of the subtree. + * + * @param action A function to produce the new generator + * @param The type of the returned generator + * @return A new generator resulting from calling the provided action on the + * result of this + */ default Generator flatMap(Function> action) { return (random, size) -> RoseTree.join(this.generate(random, size).map(action).map(g -> g.generate(random, size))); } -- cgit v1.2.3