Search code examples
scalamonadsfuturesequentialfor-comprehension

Monads being a mechanism for sequencing computations, is the below list still a monad though they are printed in a random order Scala


for {
    i <- 1 to 5
  } yield Future(println(i))

Desugared to:

List(1,2,3,4,5).map {i => Future(println(i))}

The above code prints numbers in random order.

Now, if we see the multiple definitions of Monad: a) Monad is a wrapper over an object b) Monad is a mechanism for sequencing computations

The question that I'm trying to answer is that shouldn't map operation on List monad wait for the first element in the list to be printed and only then go for the computation of the second element regardless of Future?

Sorry, it might be simple and I'm complicating it but it gets trickier for me to find simple reasoning. Answers will be much appreciated:)


Solution

  • Compare:

    for {
      _ <- Future(println(1))
      _ <- Future(println(2))
      _ <- Future(println(3))
      _ <- Future(println(4))
      _ <- Future(println(5))
    } yield ()
    

    or

    Future(println(1)).flatMap { _ =>
      Future(println(2))
    }.flatMap { _ =>
      Future(println(3))
    }.flatMap { _ =>
      Future(println(4))
    }.flatMap { _ =>
      Future(println(5))
    }
    

    with

    List(
      Future(println(1)),
      Future(println(2)),
      Future(println(3)),
      Future(println(4)),
      Future(println(5))
    )
    

    The first two create the next Future only after the former completed and made the result available. The last one creates all Futures at once (and it doesn't differ much in this regard from your example with List[Future]).

    Future (as opposed to IO from Cats Effect, Monix's Task or ZIO) is eager, so it starts execution the moment you create it. For that reason you have sequential result in the first two examples, and random order (race condition) in the third example.

    If you used IO instead of Future it would be more apparent because you wouldn't be able to just have List[IO[Unit]] and execute side effects - you would have to somehow combine the different IOs into one, and the way you would do it would make it obvious whether the effects will be sequential or parallel.

    The bottom line is - whether or not Future is a monad depends on how the .flatMap behaves (and how it behaves with combination with Future.successful), so your results doesn't invalidate the claim that Future is a monad. (You can have some doubts if you start checking its behavior with exceptions, but that is another topic).