liveBook · Manning

Gen

As explained in Functional Programming in Scala, Second Edition

Gen is a book-defined generator abstraction that represents computations for producing randomly generated values of a specified type.

Gen[A] represents a computation that produces a randomly generated A without performing generation at construction time. Its core representation is a State action whose state is RNG, so sampling a generator both yields a value and advances the random-number generator. This makes random generation an explicit, reusable value rather than an untracked effect. section 8.1.4 section 8.6

opaque type Gen[+A] = State[RNG, A]

The representation identifies a generator with a state transition: RNG is threaded through generation while the requested value is returned. section 8.1.4

Start with a generator for a simple domain, then build larger domains by composition. choose generates an Int in a requested range by mapping a nonnegative random integer into that range. unit produces a fixed value, boolean adapts the RNG’s Boolean action, and listOfN turns repeated uses of one generator into a generator of lists. The last step relies on State.sequence to transform a List of stateful generators into one stateful list generator. section 8.1.4 section 8.6

Generating a list from one generator

Given self: Gen[A], listOfN(n) first forms n copies of self. Those copies have type List[Gen[A]], equivalently List[State[RNG, A]]. Sequencing them preserves the RNG threading and produces Gen[List[A]]. A generator of the size can be composed with flatMap to select the list length before generating the elements. section 8.6 section 8.6

extension [A](self: Gen[A]) def listOfN(n: Int): Gen[List[A]] =
  State.sequence(List.fill(n)(self))

Sequencing repeated generators is the key operation that converts independently described element generation into one generator for a list. section 8.6

Generators also support choice. union selects between two generators with a generated Boolean; weighted selects according to normalized absolute weights and a generated Double. flatMap lets a generated value determine the next generator, which is how a generated list size can control list construction. section 8.6 section 8.6

The purpose of Gen is to supply arbitrary inputs to property-based tests. Prop.forAll uses a generator to test an asserted invariant over generated values. Sized generation is handled by SGen: a Gen can be made size-independent with unsized, while list and nonEmptyList use the supplied size to generate lists of corresponding lengths. section 8.6 section 8.6

Gen is not merely an isolated testing utility. Its map and flatMap resemble operations on Par, List, LazyList, Option, and State, and map obeys the same identity law. This shared behavior points toward broader abstractions and laws developed later. An extended representation can additionally carry a LazyList for exhaustive generation, allowing finite domains to be proven when every input is tested. section 8.4 section 8.6 section 8.6

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines Gen as State[RNG, A] and derives range, Boolean, list, choice, and dependent generators; later extends it with exhaustive values.
Examples
Builds ranged integers, Booleans, fixed values, sized lists, unions, weighted choices, and properties over lists and parallel computations.
What it assumes
Builds from the earlier RNG and State interfaces while using their existing nonnegative integer, Boolean, unit, flatMap, and sequence operations.
What you can do after
Build it from scratch by wrapping RNG state actions and composing them with map, flatMap, and sequence.
How it connects
Connects Gen to State, RNG, Prop, SGen, map, flatMap, List, LazyList, Option, and Par.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.