I don't think what you are after is possible in principle.
The nice for
syntax you are after translates to something like
f0.flatMap(v0 => (f1.map(v1 => f0f1(v0, v1))))
The problem here is the flatMap
part. For there to be a flatMap
you need a
Monad
, not only a Functor
, or at least something with the FlatMap
instance. But Monad
s, as opposed to Functor
s, don't compose. So there is no
way to get a nested Monad
automatically from F[_]
and G[_]
, even if both
of them are Monad
s. [1]
You might get the nice syntax using some of the monad transformers, but they
don't (and can't) exist for all monads. [2]
For monads where there are transformers, something like what you want is possible:
val l: List[Int] = List(1,2,3)
val o: Option[String] = Some("abc")
val ol: OptionT[List, String] = for {
a <- OptionT.liftF(l)
b <- OptionT.fromOption[List](o)
} yield a + b.toString
There are scala docs ([3]) if you are interested in OptionT
.
Whether that's nicer or not lies in the eyes of the beholder.
That being sad, if you need this more often, you might want to write your own helper functions along the lines of
def combine2[F[_]: Functor, G[_]: Functor, A, B, C](
fa: F[A],
gb: G[B])(
f: (A, B) => C
): F[G[C]] =
fa.map(a => gb.map(b => (f(a, b))))
If you don't want to do this, one thing you can do is what @andrey-tyukin already mentioned. But I agree that it's probably best to just nest the calls to map
.
Another thing you might want to look at is Nested
[4]. It doesn't help you in this particular case, but it might help you reduce some of the nested map
s down the road.
--
[1] http://blog.tmorris.net/posts/monads-do-not-compose/
[2] Why is there no IO transformer in Haskell?
[3] https://typelevel.org/cats/api/cats/data/OptionT.html
[4] https://typelevel.org/cats/api/cats/data/Nested.html