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 Future
s 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.