I am reading and working on the exercises on the book Funcional Programming in Scala. In the chapter about property testing, one exercise ask to implement def listOf[A](g: Gen[A]): SGen[List[A]]
, here is the relevant code:
case class Gen[+A](sample: State[RNG, A]) {
def flatMap[B](f: A ⇒ Gen[B]): Gen[B] =
Gen(sample.flatMap(f(_) sample))
/* A method alias for the function we wrote earlier. */
def listOfN(size: Int): Gen[List[A]] =
Gen.listOfN(size, this)
def listOfN(size: Gen[Int]): Gen[List[A]] =
size flatMap (Gen listOfN (_, this))
}
object Gen {
def listOfN[A](n: Int, g: Gen[A]): Gen[List[A]] =
Gen(State.sequence(List.fill(n)(g.sample)))
def listOf[A](g: Gen[A]): SGen[List[A]] =
// SGen { n ⇒ g.listOfN(n) }
// SGen{listOfN(_, g)}
}
case class SGen[+A](forSize: Int ⇒ Gen[A])
As you can see, listOf[A](g: Gen[A]): SGen[List[A]]
is implemented in two ways, the second one is the one I thought, and the first one is the solution provided by the book.
My question is, is there any difference between creating that SGen
via the companion object and creating it using the listOfN
method on the generator g
? I suppose as long as both implementations ends up using g
to generate the values, there is little difference.
There's really no practical difference in this example. You can see from the implementation that Gen.listOfN(size: Int)
is just calling the companion object implementation. One of the advantages of the method alias is that you could use simpler syntax in the companion object like this:
object Gen {
...
def listOf[A](g: Gen[A]): SGen[List[A]] =
SGen(g.listOfN)
}
Having these different syntax options can make a bigger impact on clarity when users make larger compositions.