Scalacheck: Generate list corresponding to list of generators
Asked Answered
S

4

9

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.

Sourdough answered 25/11, 2012 at 7:27 Comment(0)
T
14

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]] = 
     ...
Terceira answered 25/11, 2012 at 10:56 Comment(1)
Works fine! I had to explicitly add the implicit argument because of ambiguity with another method. val listGen = Gen.sequence(gens)(util.Buildable.buildableList)Sourdough
W
3

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)
Whidah answered 5/1, 2017 at 4:9 Comment(0)
S
1

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)
Spellbinder answered 25/11, 2012 at 10:16 Comment(2)
I don't want create samples manually. It will be done by scalacheck when I use the generator as forAll(listGen){ list => <list property> }Sourdough
Ah sorry about that, didn't realise that scalacheck creates the samplesSpellbinder
A
0

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.

Aeriell answered 20/12, 2012 at 20:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.