This isn’t the only way to structure a property-testing library. My Monte version of Hypothesis-style testing (which predates this article but was directly inspired by Hypothesis) further encapsulates random numbers. Low-level generators are wrapped by the “entropy” ad-hoc interface first, and then these wrapped generators are passed to implementors of the Arb interface, which generate arbitrary shrinkable values. Implementors of “entropy” and Arb are mutable, but the arbitrary shrinkable values are immutable.
This has roughly the same shape as Hypothesis, but behaves very differently. As Hypothesis alters its entropy, it searches through entire histories of execution, possibly affecting multiple units. My library only changes individual values as entropy changes, and isolates each search to a single unit. This is partially because Monte’s auditors can be used to prove that the units under test are isolated in their original modules, and partially because the typical Monte object is immutable by default — in contrast to Python — which let us more precisely skip repeated test inputs.