Skip to content

Commit

Permalink
Add explicit suchThat to Gen.conatainerOf combinators.
Browse files Browse the repository at this point in the history
This also restricts Gen.containerOf to only handle
types that are implicitly convertible to Traversable.
This is required in order to be able to inspect
simplified containers.
  • Loading branch information
rickynils committed Oct 31, 2013
1 parent e5b02d4 commit d2e6f07
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 59 deletions.
16 changes: 9 additions & 7 deletions src/main/scala/org/scalacheck/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,18 @@ object Arbitrary {
implicit def arbEither[T, U](implicit at: Arbitrary[T], au: Arbitrary[U]): Arbitrary[Either[T, U]] =
Arbitrary(oneOf(arbitrary[T].map(Left(_)), arbitrary[U].map(Right(_))))

/** Arbitrary instance of any [[org.scalacheck.util.Buildable]] container (such as lists, arrays,
* streams, etc). The maximum size of the container depends on the size
* generation parameter. */
implicit def arbContainer[C[_],T](implicit a: Arbitrary[T], b: Buildable[T,C]
/** Arbitrary instance of any [[org.scalacheck.util.Buildable]] container
* (such as lists, arrays, streams, etc). The maximum size of the container
* depends on the size generation parameter. */
implicit def arbContainer[C[_],T](implicit
a: Arbitrary[T], b: Buildable[T,C], t: C[T] => Traversable[T]
): Arbitrary[C[T]] = Arbitrary(containerOf[C,T](arbitrary[T]))

/** Arbitrary instance of any [[org.scalacheck.util.Buildable2]] container (such as maps, etc).
* The maximum size of the container depends on the size
/** Arbitrary instance of any [[org.scalacheck.util.Buildable2]] container
* (such as maps, etc). The maximum size of the container depends on the size
* generation parameter. */
implicit def arbContainer2[C[_,_],T,U](implicit a: Arbitrary[(T,U)], b: Buildable2[T,U,C]
implicit def arbContainer2[C[_,_],T,U](implicit
a: Arbitrary[(T,U)], b: Buildable2[T,U,C], t: C[T,U] => Traversable[(T,U)]
): Arbitrary[C[T,U]] = Arbitrary(containerOf[C,T,U](arbitrary[(T,U)]))

// Functions //
Expand Down
119 changes: 71 additions & 48 deletions src/main/scala/org/scalacheck/Gen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,7 @@ object Gen {
case (rs,g) => g.doApply(p).flatMap(r => rs.map(_ :+ r))
}
}
val sieve = gs.foldLeft((_:T) => false) { case (s,g) =>
x:T => s(x) || g.sieveCopy(x)
}
g.map(b.fromIterable) // TODO .suchThat(_.forall(sieve))
g.map(b.fromIterable)
}

/** Sequences generators. If any of the given generators fails, the
Expand All @@ -280,10 +277,7 @@ object Gen {
case (rs,g) => g.doApply(p).flatMap(r => rs.map(_ :+ r))
}
}
val sieve = gs.foldLeft((_:T) => false) { case (s,g) =>
x:T => s(x) || g.sieveCopy(x)
}
g.map(b.fromIterable) // TODO .suchThat(_.forall(sieve))
g.map(b.fromIterable)
}

/** Wraps a generator lazily. The given parameter is only evaluated once,
Expand Down Expand Up @@ -348,57 +342,84 @@ object Gen {

//// List Generators ////

/** Generates a container of any type for which there exists an implicit
/** Generates a container of any Traversable type for which there exists an implicit
* [[org.scalacheck.util.Buildable]] instance. The elements in the container will
* be generated by the given generator. The size of the generated container
* is given by `n`. If the given generator fails generating a value, the
* complete container gnerator will also fail. */
def containerOfN[C[_],T](n: Int, g: Gen[T])(implicit b: Buildable[T,C]
): Gen[C[T]] = sequence[C,T](Traversable.fill(n)(g))

/** Generates a container of any type for which there exists an implicit
* [[org.scalacheck.util.Buildable]] instance. The elements in the container
* will be generated by the given generator. The size of the container is
* bounded by the size parameter used when generating values. */
def containerOf[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] =
sized(size => choose(0,size).flatMap(containerOfN[C,T](_,g)))
* complete container generator will also fail. */
def containerOfN[C[_],T](n: Int, g: Gen[T])(implicit
evb: Buildable[T,C], evt: C[T] => Traversable[T]
): Gen[C[T]] =
sequence[C,T](Traversable.fill(n)(g)) suchThat { c =>
c.size == n && c.forall(g.sieveCopy)
}

/** Generates a non-empty container of any type for which there exists an
/** Generates a container of any Traversable type for which there exists an
* implicit [[org.scalacheck.util.Buildable]] instance. The elements in the
* container will be generated by the given generator. The size of the
* container is bounded by the size parameter used when generating values. */
def nonEmptyContainerOf[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] =
sized(size => choose(1,size).flatMap(containerOfN[C,T](_,g)))
def containerOf[C[_],T](g: Gen[T])(implicit
evb: Buildable[T,C], evt: C[T] => Traversable[T]
): Gen[C[T]] =
sized(s => choose(0,s).flatMap(containerOfN[C,T](_,g))) suchThat { c =>
c.forall(g.sieveCopy)
}

/** Generates a non-empty container of any type for which there exists an
* implicit [[org.scalacheck.util.Buildable]] instance. The elements in the
* container will be generated by the given generator. The size of the
* container is bounded by the size parameter used when generating values. */
@deprecated("Use Gen.nonEmptyContainerOf instead", "1.11.0")
def containerOf1[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] =
nonEmptyContainerOf[C,T](g)
/** Generates a non-empty container of any Traversable type for which there
* exists an implicit [[org.scalacheck.util.Buildable]] instance. The
* elements in the container will be generated by the given generator. The
* size of the container is bounded by the size parameter used when
* generating values. */
def nonEmptyContainerOf[C[_],T](g: Gen[T])(implicit
evb: Buildable[T,C], evt: C[T] => Traversable[T]
): Gen[C[T]] =
sized(s => choose(1,s).flatMap(containerOfN[C,T](_,g))) suchThat { c =>
c.size > 0 && c.forall(g.sieveCopy)
}

/** Generates a container of any type for which there exists an implicit
* [[org.scalacheck.util.Buildable2]] instance. The elements in the container will
* be generated by the given generator. The size of the generated container
* is given by `n`. If the given generator fails generating a value, the
* complete container gnerator will also fail. */
def containerOfN[C[_,_],T,U](n: Int, g: Gen[(T,U)])(implicit b: Buildable2[T,U,C]
): Gen[C[T,U]] = sequence[C,T,U](Traversable.fill(n)(g))
/** Generates a non-empty container of any Traversable type for which there
* exists an implicit [[org.scalacheck.util.Buildable]] instance. The
* elements in the container will be generated by the given generator. The
* size of the container is bounded by the size parameter used when
* generating values. */
@deprecated("Use Gen.nonEmptyContainerOf instead", "1.11.0")
def containerOf1[C[_],T](g: Gen[T])(implicit
evb: Buildable[T,C], evt: C[T] => Traversable[T]
): Gen[C[T]] = nonEmptyContainerOf[C,T](g)

/** Generates a container of any Traversable type for which there exists an
* implicit [[org.scalacheck.util.Buildable2]] instance. The elements in
* the container will be generated by the given generator. The size of the
* generated container is given by `n`. If the given generator fails
* generating a value, the complete container generator will also fail. */
def containerOfN[C[_,_],T,U](n: Int, g: Gen[(T,U)])(implicit
evb: Buildable2[T,U,C], evt: C[T,U] => Traversable[(T,U)]
): Gen[C[T,U]] =
sequence[C,T,U](Traversable.fill(n)(g)).suchThat { c =>
c.size == n && c.forall(g.sieveCopy)
}

/** Generates a container of any type for which there exists an implicit
* <code>Buildable2</code> instance. The elements in the container will
* be generated by the given generator. The size of the container is
* bounded by the size parameter used when generating values. */
def containerOf[C[_,_],T,U](g: Gen[(T,U)])(implicit b: Buildable2[T,U,C]): Gen[C[T,U]] =
sized(size => for(n <- choose(0,size); c <- containerOfN[C,T,U](n,g)) yield c)
/** Generates a container of any Traversable type for which there exists
* an implicit <code>Buildable2</code> instance. The elements in the
* container will be generated by the given generator. The size of the
* container is bounded by the size parameter used when generating values. */
def containerOf[C[_,_],T,U](g: Gen[(T,U)])(implicit
evb: Buildable2[T,U,C], evt: C[T,U] => Traversable[(T,U)]
): Gen[C[T,U]] =
sized(s => choose(0,s).flatMap(containerOfN[C,T,U](_,g))) suchThat { c =>
c.forall(g.sieveCopy)
}

/** Generates a non-empty container of any type for which there exists an
* implicit <code>Buildable2</code> instance. The elements in the container
* will be generated by the given generator. The size of the container is
* bounded by the size parameter used when generating values. */
def nonEmptyContainerOf[C[_,_],T,U](g: Gen[(T,U)])(implicit b: Buildable2[T,U,C]): Gen[C[T,U]] =
sized(size => for(n <- choose(1,size); c <- containerOfN[C,T,U](n,g)) yield c)
def nonEmptyContainerOf[C[_,_],T,U](g: Gen[(T,U)])(implicit
evb: Buildable2[T,U,C], evt: C[T,U] => Traversable[(T,U)]
): Gen[C[T,U]] =
sized(s => choose(1,s).flatMap(containerOfN[C,T,U](_,g))) suchThat { c =>
c.size > 0 && c.forall(g.sieveCopy)
}

/** Generates a list of random length. The maximum length depends on the
* size parameter. This method is equal to calling
Expand Down Expand Up @@ -482,16 +503,18 @@ object Gen {

/* Generates a string that starts with a lower-case alpha character,
* and only contains alphanumerical characters */
def identifier: Gen[String] = for {
def identifier: Gen[String] = (for {
c <- alphaLowerChar
cs <- listOf(alphaNumChar)
} yield (c::cs).mkString // TODO suchThat
} yield (c::cs).mkString).suchThat(_.forall(c => c.isLetter || c.isDigit))

/* Generates a string of alpha characters */
def alphaStr: Gen[String] = listOf(alphaChar).map(_.mkString) // TODO suchThat
def alphaStr: Gen[String] =
listOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter))

/* Generates a string of digits */
def numStr: Gen[String] = listOf(numChar).map(_.mkString) // TODO suchThat
def numStr: Gen[String] =
listOf(numChar).map(_.mkString).suchThat(_.forall(_.isDigit))


//// Number Generators ////
Expand Down
1 change: 0 additions & 1 deletion src/main/scala/org/scalacheck/util/Buildable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ trait Buildable[T,C[_]] {
b ++= it
b.result()
}
// TODO def toIterable
}

trait Buildable2[T,U,C[_,_]] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ package util
import collection._

object BuildableSpecification {
def container[C[_]](implicit ev: Buildable[String, C]) =
Gen.containerOf[C, String](Gen.alphaStr)
def container[C[_]](implicit
evb: Buildable[String, C],
evt: C[String] => Traversable[String]
) = Gen.containerOf[C, String](Gen.alphaStr)

implicit val listGen: Gen[List[String]] = container[List]

Expand All @@ -41,4 +43,4 @@ object BuildableSpecification {
implicit val iterableGen: Gen[immutable.Iterable[String]] = container[immutable.Iterable]

implicit val trieIteratorGen: Gen[immutable.Queue[String]] = container[immutable.Queue]
}
}

0 comments on commit d2e6f07

Please sign in to comment.