如何在Scala中堆叠应用函子

26

当您的计算步骤是独立的时候,可应用函子常被提及作为单子的替代品。它们经常被提到的优点之一是,在想要堆叠 applicatives 时,您不需要 transformer,因为 F[G[X]] 总是一个可应用函子。

假设我有以下函数:

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

我希望能够优雅地堆叠以获取 Future[Option[User]]Future[Option[Data]],并将其与 process 映射。

到目前为止,我只想到了这个(使用Cats):

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

但我确定这远非理想之选。有没有更优雅和通用的方法来实现相同的功能?

1个回答

3
这里最困难的部分是类型推断。这是我所能做到的最好。
  // 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 _)

谢谢,如果可能的话,我想类似于Eff/Emm单子的东西会很方便去除样板代码。 - kciesielski
我认为在这里最简单的方法可能是只需使用OptionT,但你也可以使用Eff(或类似的Emm)来执行(send(getDataOption) |@| send(getUserFuture)).map(process _).runOption.detach.run,以获得一个最终的Future[Option[A]] - Eric
没错。不过,我想继续使用applicatives。我的问题的重点是看到赞扬的applicative属性的实际应用,即它们可以在不使用transformers的情况下堆叠。 - kciesielski
它们确实可以,但在Scala中可能不是最容易的事情 :-( - Eric
1
看起来你刚刚为Eff添加了非常好的应用支持。谢谢,Eric!https://atnos-org.github.io/eff-cats/org.atnos.site.ApplicativeEvaluation.html - kciesielski
1
说实话,我仍然很惊讶它能够正常工作!如果您发现任何问题,请及时报告。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接