diff --git a/jvm/src/test/scala/org/scalacheck/GenSpecification.scala b/jvm/src/test/scala/org/scalacheck/GenSpecification.scala index 907b9fd7e..931b0a8d7 100644 --- a/jvm/src/test/scala/org/scalacheck/GenSpecification.scala +++ b/jvm/src/test/scala/org/scalacheck/GenSpecification.scala @@ -420,6 +420,28 @@ object GenSpecification extends Properties("Gen") with GenSpecificationVersionSp } } + property("fix is like lzy") = forAll { (seeds: List[Seed]) => + lazy val lzyGen: Gen[List[Int]] = { + Gen.choose(0, 10).flatMap { idx => + if (idx < 5) lzyGen.map(idx :: _) + else Gen.const(idx :: Nil) + } + } + + val fixGen = + Gen.fix[List[Int]] { recurse => + Gen.choose(0, 10).flatMap { idx => + if (idx < 5) recurse.map(idx :: _) + else Gen.const(idx :: Nil) + } + } + + val params = Gen.Parameters.default + seeds.forall { seed => + lzyGen.pureApply(params, seed) == fixGen.pureApply(params, seed) + } + } + property("uuid version 4") = forAll(uuid) { _.version == 4 } property("uuid unique") = forAll(uuid, uuid) { diff --git a/src/main/scala/org/scalacheck/Gen.scala b/src/main/scala/org/scalacheck/Gen.scala index eea9757f4..5b72a2623 100644 --- a/src/main/scala/org/scalacheck/Gen.scala +++ b/src/main/scala/org/scalacheck/Gen.scala @@ -473,6 +473,23 @@ object Gen extends GenArities with GenVersionSpecific { /** A generator that never generates a value */ def fail[T]: Gen[T] = gen((p, seed) => failed[T](seed)) + + /** + * A fixed point generator. This is useful for making recusive structures + * e.g. + * + * Gen.fix[List[Int]] { recurse => + * Gen.choose(0, 10).flatMap { idx => + * if (idx < 5) recurse.map(idx :: _) + * else Gen.const(idx :: Nil) + * } + * } + */ + def fix[A](fn: Gen[A] => Gen[A]): Gen[A] = { + lazy val result: Gen[A] = lzy(fn(result)) + result + } + /** A result that never contains a value */ private[scalacheck] def failed[T](seed0: Seed): R[T] = new R[T] {