dips
dips

Reputation: 1637

Scalacheck: Generate list corresponding to list of generators

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

Answers (4)

Joseph Lust
Joseph Lust

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

Ptharien&#39;s Flame
Ptharien&#39;s Flame

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

Plasty Grove
Plasty Grove

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

Eric
Eric

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

Related Questions