1

ScalaCheck generators work with the syntactic sugar in Scala's for expressions:

for( s1 <- Gen.choose(1, 10); s2 <- Gen.choose(10, 100) ) yield ( s1, s2 ) 

I'd like to be able to mix "traditional" Scala for expressions with ScalaCheck expressions. For example:

for( s0 <- 0 until 10; s1 <- Gen.choose(1, 10); s2 <- Gen.choose(10, 100) ) yield ( s0, s1, s2 ) 

However, this won't compile, since the 0 until 10 expressions isn't a type of Gen.

How can I achieve this (and Seq and/or Traversable expressions in general) within the same for loop?

EDIT: The consensus in the answers seems to be that the kind of syntactic sugar I'm looking for is not possible.

However, could this not be done with some form of stateful Gen? In particular, one which used the state monad?

5
  • 3
    I'm not clear on what you're trying to achieve here. What's the type of the expression you're aiming for? (the first for expression in your question has type Gen[(Int, Int)] - what should be the second's?) Commented Apr 13, 2016 at 16:52
  • I'd like the yielded expression to have type Gen[(Int,Int,Int)], but for the values of s0 to exhaustively range from 0 until 10, while s1 and s2 are chosen randomly each time. Commented Apr 13, 2016 at 17:47
  • I see - so you simply want to "make sure" that all values of (1 to 10) are used, instead of choosing randomly from this range. I don't know of a way to do that... Commented Apr 13, 2016 at 18:17
  • @user217281728 Do you have any feedback on the answers you've got so far? Commented Apr 18, 2016 at 13:25
  • @Jubobs - I was hoping for a response to my edit about the state monad. Commented Apr 19, 2016 at 10:29

2 Answers 2

2

I'd like to be able to mix "traditional" Scala for expressions with ScalaCheck expressions.

You cannot do that; at least, not as you suggest. What you can do is to define a generator that produces 10-long lists (i.e. lists of length 10) of triples, in which

  • the first element is not random (but instead ranges from 0 to 10),
  • the second element is randomly chosen between 1 and 10,
  • the third element is randomly chosen between 10 and 100.

Generator implementation

I'm assuming that org.scalacheck.Gen is in scope.

  1. Define a generator for a pair composed of the second and third elements of a triple:

    val pairGen: Gen[(Int, Int)] = for { s1 <- Gen.choose(1, 10) s2 <- Gen.choose(10, 100) } yield (s1, s2) 
  2. Define a generator for a 10-long list of such pairs:

    val listOfPairsGen: Gen[List[(Int, Int)]] = Gen.listOfN(10, pairGen) 
  3. Define

    val intList: List[Int] = (0 until 10).toList 
  4. Zip intList with the result of listOfPairsGen, and "flatten each element to a triple":

    val myGen: Gen[List[(Int, Int, Int)]] = listOfPairsGen map { list: List[(Int, Int)] => (intList zip list) map { case (a, (b, c)) => (a, b, c) } } 

Examples

scala> myGen.sample.head res0: List[(Int, Int, Int)] = List((0,2,58), (1,10,34), (2,3,94), (3,2,91), (4,6,15), (5,7,99), (6,4,82), (7,10,69), (8,8,78), (9,10,27)) scala> myGen.sample.get res1: List[(Int, Int, Int)] = List((0,2,56), (1,2,83), (2,4,76), (3,4,87), (4,4,55), (5,6,80), (6,4,94), (7,7,67), (8,10,92), (9,4,84)) scala> myGen.sample.get res2: List[(Int, Int, Int)] = List((0,10,40), (1,9,48), (2,10,63), (3,5,100), (4,5,67), (5,4,73), (6,8,56), (7,6,58), (8,6,82), (9,10,86)) scala> myGen.sample.get res3: List[(Int, Int, Int)] = List((0,6,56), (1,7,94), (2,4,40), (3,7,27), (4,1,91), (5,3,50), (6,1,70), (7,6,90), (8,7,23), (9,7,49)) 
Sign up to request clarification or add additional context in comments.

2 Comments

The equivalent (but easier) approach will be to wrap Property validation in (1 to 10).forall { s1 =>
@Aivean True, but whether the OP needs this generator in the context of property-based tests is unclear. S/he may want to use this generator for other purposes.
1

As Jubobs already explained, you cannot

mix "traditional" Scala for expressions with ScalaCheck expressions

However, you could achieve the described result with a somewhat less idiomatic approach using an index:

def indexedGenerator = { val index = new AtomicInteger(0) for (s1 <- Gen.choose(1, 10); s2 <- Gen.choose(10, 100)) yield (index.getAndIncrement(), s1, s2) } val gen = indexedGenerator println(gen.sample) |-> Some((0,1,60)) println(gen.sample) |-> Some((1,8,82)) println(gen.sample) |-> Some((2,9,29)) println(gen.sample) |-> Some((3,6,76)) println(gen.sample) |-> Some((4,5,32)) 

Though you would have to be cautious about when and where to instantiate a Generator like this, as the count var will not be reset for every property check. Thus, you need to create a new local instance every time you need the index to start at 0.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.