26

Applicative functors are often mentioned as an alternative to monads when your computation steps are independent. One of their often-mentioned advantages is that you don't need transformers when you want to stack applicatives, because F[G[X]] is always also an applicative. Let's say I have following functions:

def getDataOption(): Option[Data]
def getUserFuture(): Future[User]
def process(data: Data, user: User)

I would like to have elegant stacking in order to get a Future[Option[User]] and Future[Option[Data]] and map that with process.

So far I only came up with this (using Cats):

Applicative[Future]
  .compose[Option]
    .map2(
      Applicative[Future].pure(getDataOption()),
      getUserFuture().map(Applicative[Option].pure))(process)

but I'm sure it's far from ideal. Is there a more elegant and generic way to achieve the same?

kciesielski
  • 1,178
  • 9
  • 18

1 Answers1

3

The most difficult thing is type inference here. This is the best I could do

  // for the Applicative[Future[Option[?]]
  import cats.Applicative

  implicit val fo = {
    import cats.std.future._
    import cats.std.option._
    Applicative[Future].compose[Option]
  }

  // for the |@| syntax
  import cats.syntax.cartesian._

  // to guide type inference
  type FutureOption[A] = Future[Option[A]]

  ((Future(getDataOption): FutureOption[Data]) |@|
    getUserFuture.map(Option.apply)).map(process _)
Eric
  • 15,494
  • 38
  • 61
  • Thanks, I guess something similar to the Eff/Emm monad would be handy to strip the boilerplate, if it's possible. – kciesielski Apr 22 '16 at 08:48
  • I think that the easiest thing to do here is might be to just grab `OptionT`, but you probably can do `(send(getDataOption) |@| send(getUserFuture)).map(process _).runOption.detach.run` to get a final `Future[Option[A]]` with Eff (and something similar with Emm). – Eric Apr 22 '16 at 08:56
  • Right. However, I wanted to stay with applicatives. The point of my question was to see a practical application of praised applicative property, that they can be stacked without using transformers. – kciesielski Apr 22 '16 at 13:19
  • They can indeed, but they might not be the easiest thing to do in Scala :-( – Eric Apr 22 '16 at 13:22
  • 1
    Looks like you just added a very nice applicative support to Eff. Thanks, Eric! https://atnos-org.github.io/eff-cats/org.atnos.site.ApplicativeEvaluation.html – kciesielski May 17 '16 at 06:10
  • 1
    I am still amazed that it works at all to be honest! So please report if you find any issues. – Eric May 17 '16 at 06:18