Reputation: 1637
I want to generate a list of integers corresponding to a list of generators in ScalaCheck
.
import org.scalacheck._
import Arbitrary.arbitrary
val smallInt = Gen.choose(0,10)
val bigInt = Gen.choose(1000, 1000000)
val zeroOrOneInt = Gen.choose(0, 1)
val smallEvenInt = smallInt suchThat (_ % 2 == 0)
val gens = List(smallInt, bigInt, zeroOrOneInt, smallEvenInt)
//val listGen: Gen[Int] = ??
//println(listGen.sample) //should print something like List(2, 2000, 0, 6)
For the given gens
, I would like to create a generator listGen
whose valid sample can be List(2, 2000, 0, 6)
.
Here is my first attempt using tuples.
val gensTuple = (smallInt, bigInt, zeroOrOneInt, smallEvenInt)
val tupleGen = for {
a <- gensTuple._1
b <- gensTuple._2
c <- gensTuple._3
d <- gensTuple._4
} yield (a, b, c, d)
println(tupleGen.sample) // prints Some((1,318091,0,6))
This works, but I don't want to use tuples since the list of generators(gens
) is created dynamically
and the size of the list is not fixed. Is there a way to do it with Lists?
I want the use the generator of the list(listGen
) in scalacheck forAll
property checking.
This looks like a toy problem but this is the best I could do to create a standalone snippet reproducing the actual issue I am facing.
Upvotes: 9
Views: 5258
Reputation: 19975
Just use Gen.sequence
, but be careful as it will try to return a java.util.ArrayList[T]
if you don't fully parameterize it (bug).
Full working example:
def genIntList(): Gen[List[Int]] = {
val gens = List(Gen.chooseNum(1, 2), Gen.chooseNum(3, 4))
Gen.sequence[List[Int], Int](gens)
}
println(genIntList.sample.get) // prints: List(1,4)
Upvotes: 3
Reputation: 3246
For a more theoretical answer: the method you want is traverse
, which is equivalent to sequence compose map
although it might be more efficient. It is of the general form:
def traverse[C[_]: Traverse, F[_]: Applicative, A, B](f: A => F[B], t: C[A]): F[C[B]]
It behaves like map
but allows you to carry around some extra Applicative
structure during the traversal, sequencing it along the way.
Upvotes: 0
Reputation: 2877
EDIT: Please disregard, this doesn't answer the asker's question
I can't comment on posts yet, so I'll have to venture a guess here. I presume the function 'sample' applies to the generators
Any reason why you can't do:
gens map (t=>t.sample)
Upvotes: 1
Reputation: 15557
How about using the Gen.sequence
method? It transforms an Iterable[Gen[T]]
into a Gen[C[T]]
, where C
can be List
:
def sequence[C[_],T](gs: Iterable[Gen[T]])(implicit b: Buildable[T,C]): Gen[C[T]] =
...
Upvotes: 14