Как складывать аппликативные функции в Scala

Аппликативные функторы часто упоминаются как альтернатива монадам, когда ваши этапы вычислений независимы. Одним из их часто упоминаемых преимуществ является то, что вам не нужны трансформаторы, если вы хотите укладывать аппликаторы, потому что F[G[X]] всегда также является аппликативным. Скажем, у меня есть следующие функции:

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

Я хотел бы иметь элегантную укладку, чтобы получить Future[Option[User]] и Future[Option[Data]] и сопоставить это с process.

До сих пор я только придумал это (используя Кошек):

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

но я уверен, что это далеко не идеально. Есть ли более элегантный и общий способ добиться того же?

Ответ 1

Самое сложное - это вывод типа здесь. Это лучшее, что я мог сделать

  // 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 _)