Slow Harry
Slow Harry

Reputation: 1897

How to express the following nested futures with for-comprehension

My question: is it possible to simplify this nested expression with for-comprehension ?

run(dbAction).flatMap(insertedJobs => {
  Future.sequence(insertedJobs.map { job =>
    recordingBean.findStreamsForInterval(job.mediaSource, job.begin, job.end) map { stream =>
      if (stream.nonEmpty) {
        recordingBean.findRecordingLocationsForInterval(stream.head.stream, job.begin, job.end).map(recordingLocations =>
          recordingLocations.map(_.size.getOrElse(0L)).sum).flatMap { expectedSizeInBytes =>
          updateSize(job.id.get, expectedSizeInBytes)
        }
        job
      } else {
        job
      }
    }
  })
})

Upvotes: 0

Views: 747

Answers (1)

sarveshseri
sarveshseri

Reputation: 13985

For comprehensions are powerful but they are not an almighty nesting-killer you think they are.

for-comprehension are useful till you are not mixing SeqLike monads with non-SeqLike monads. But once you mix them, you are back to nesting world. And it will be even worse than before because for-comprehensions hide away the details.

Lets take some examples,

val listOfListOfInt = List(List(1, 2, 3), List(2, 3, 4))

val listOfInt = for {
  loi <- listOfListOfInt
  i <- loi
} yield i + 4

// This will work just fine
// listOfInt: List[Int] = List(5, 6, 7, 6, 7, 8) 

Now lets mix List with Future,

val futureOfList = Future({ List(1, 2, 3) })

val iWillNotCompile = for {
  l <- futureOfList
  i <- list
} yield i + 4

// the above is equivalent to writing,
val iWillNotCompile = futureOfList.flatMap(l => l.map(i => i + 4))

// would have been following but does not make sense
// `Success(5, 6, 7)`

The above code will not compile and actually it should not compile. As Future is a non-SeqLike monad. I mean, if the above code worked it would have been a Success(5, 6, 7), which does not make sense.

Similarly, following code will not work

val listOfFuture = List(Future({ 1 }), Future({ 2 }), Future({ 3 }) )

val iWillNotCompile = for {
  f <- listOfFuture
  i <- f
} yield i + 4

// this is equivalent to
val iWillNotCompile = listOfFuture.flatMap(f => f.map(i => i + 4)

// should have been following and it makes sense
// List(Success(5), Success(6), Success(7))

This case is a lot different from the previous one, as it confirms to common sense but still it will not work. The reason is that SeqLike-Monads and Futures have very very different implementations of flatMap and flatten and map.

So... if are dealing with a mix of List, Future, Option, Try etc, stay away from for-comprehension. Just try to write your code in a more clever way.

Upvotes: 2

Related Questions