Let's get our imports out of the way.
import scala.util.Random.nextInt
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz._, Scalaz._
import scalaz.effect.IO
// API specialized to Future
def rndF(n: Int): Future[List[Int]] = {
val m = Future(nextInt(100))
Future.sequence(List.fill(n)(m))
}
// API abstracted over Monad (allegedly)
def rnd[M[_]](n: Int)(implicit M: Monad[M]): M[List[Int]] = {
val m = M.point(nextInt(100))
List.fill(n)(m).sequence
}
So let's confirm that the behavior is consistent between the implementations when using Future
.
scala> Await.result(rndF(10), Duration.Inf)
res6: List[Int] = List(90, 90, 90, 90, 90, 90, 90, 90, 90, 90)
scala> Await.result(rnd[Future](10), Duration.Inf)
res7: List[Int] = List(33, 33, 33, 33, 33, 33, 33, 33, 33, 33)
As expected. Now with Id
and Need
.
scala> rnd[Id](10)
res8: scalaz.Scalaz.Id[List[Int]] = List(96, 96, 96, 96, 96, 96, 96, 96, 96, 96)
scala> rnd[Need](10).value
res9: List[Int] = List(18, 18, 18, 18, 18, 18, 18, 18, 18, 18)
But what about data types that capture computations rather than values?
scala> rnd[IO](10).unsafePerformIO
res10: List[Int] = List(40, 49, 79, 36, 77, 22, 62, 89, 63, 11)
scala> rnd[Name](10).value
res11: List[Int] = List(96, 66, 69, 86, 0, 51, 4, 85, 30, 56)