diff options
author | Carlo Zancanaro <carlo@zancanaro.id.au> | 2015-06-01 16:40:53 +1000 |
---|---|---|
committer | Carlo Zancanaro <carlo@zancanaro.id.au> | 2015-06-01 16:40:53 +1000 |
commit | 110eebaf15768d31d26be3fa52abc8fc25f8488f (patch) | |
tree | 6d737aa30a96de6d3ef1198b0211d8f92d4064e3 /src | |
parent | daeae9a1d184d733d278857079c09af19add80c6 (diff) |
Add some docs to the Generator interface
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/au/id/zancanaro/javacheck/Generator.java | 89 |
1 files 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 <T> The type generated by this generator. + */ public interface Generator<T> { + /** + * 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<T> generate(Random random, int size); + /** + * A generator which simply generates the provided value. Does not shrink. + * + * @param value The value to generate + * @param <T> The type of the generated value + * @return A {@link Generator} which generates the provided value + */ static <T> Generator<T> 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 <T> The parameter type of the generated {@link List} + * @return A {@link Generator} returning a {@link List} + */ @SafeVarargs static <T> Generator<List<T>> tuple(Generator<T>... generators) { return (random, size) -> { @@ -24,21 +73,57 @@ public interface Generator<T> { }; } - static <T> Generator<List<T>> list(int count, Generator<T> 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 <T> The parameter type of the generated {@link List} + * @return A {@link Generator} returning a {@link List} + */ + static <T> Generator<List<T>> list(int count, Generator<T> generator) { return (random, size) -> { @SuppressWarnings("unchecked") RoseTree<T>[] result = (RoseTree<T>[]) 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 <R> The result of the transformation + * @return A new generator resulting from mapping f over this + */ default <R> Generator<R> map(Function<T, R> 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 <R> The type of the returned generator + * @return A new generator resulting from calling the provided action on the + * result of this + */ default <R> Generator<R> flatMap(Function<T, Generator<R>> action) { return (random, size) -> RoseTree.join(this.generate(random, size).map(action).map(g -> g.generate(random, size))); } |