JasperT JasperT - 4 months ago 9
Scala Question

Why is there no foldLeft or foreach on scalacheck Gen

I have an immutable datastructure in scala, that contains a list of objects, that i would like to test with scalacheck.

An insert on this object returns a new object. How do I insert multiple objects in this datastructure? With other words, how do I write a generator for a random instance of my datastructure?

If the type is H[A] where A is the type of object, I tried doing something like:

var heap = empty
arbitrary[A] map (x => heap = insert(x, heap))


but this did not work.

Answer

Here is a very simple, immutable data structure equipped with an insert operation:

final class WrappedList[A] private (underlying: List[A]) {

  def insert[B>:A](elem: B): WrappedList[B] = new WrappedList(elem :: underlying)

  override def toString: String = "WrappedList(" + underlying.mkString(", ") + ")"

}

object WrappedList {

  def empty[A]: WrappedList[A] = new WrappedList(Nil)

}

As a simple example, let's see how you could define a generator of WrappedList[String]. First, define a generator for a List[String]:

val genStrings: Gen[List[String]] =
    Gen.listOfN[String](10, Gen.oneOf("foo", "bar", "baz", "qux", "quux"))

Then, define a generator of WrappedList[String] that takes a list of strings generated by genStrings, and folds over that list to insert each element in an initially empty WrappedList[String]:

val genWrappedListOfStrings = genStrings
    .flatMap( _.foldRight(WrappedList.empty[String]) { (string, wrappedList) =>
      wrappedList insert string)
    }

You're done!

scala> genWrappedListOfStrings.sample.head
res0: WrappedList[String] = WrappedList(qux, bar, bar, baz, quux, foo, qux, qux, foo, qux)

scala> genWrappedListOfStrings.sample.head
res1: WrappedList[String] = WrappedList(qux, foo, qux, baz, qux, qux, quux, quux, qux, quux)

Note that there is no need for a var.