有没有办法压缩不同类型的嵌套monad?

时间:2014-07-21 21:37:00

标签: scala functional-programming monads

我不确定如何描述这个问题,所以我只会显示类型签名。

我有以下实例:

val x:Future[F[Future[F[B]]]] = ???

我想要一个实例:

val y:Future[F[B]] = ???

F是Monad,所以我有以下方法:

def pure[A](a:A):F[A] = ???
def flatMap[A, B](fa:F[A], f:A => F[B]):F[B] = ???
def map[A, B](fa:F[A], f:A => B):F[B] = flatMap(fa, (a:A) => pure(f(a)))

我认为以下内容应该有效,但感觉不对:

x.flatMap { fWithFuture =>
  val p = Promise[F[B]]
  flatMap(fWithFuture, (futureF: Future[F[B]]) => {
    p.completeWith(futureF)
    pure(())
  })
  p.future
}

我缺少一个概念吗?

一些背景资料。我试图定义这样的函数:

def flatMap[A, B](fa:Future[F[A]], f: A => Future[F[B]]):Future[F[B]] = ???

也许这在概念上是一件奇怪的事情。欢迎任何关于有用抽象的提示。

2 个答案:

答案 0 :(得分:7)

正如Rex Kerr上面所说,你经常可以使用monad变换器来处理你发现自己有像这样的交替层的情况。例如,如果此处FOption,您可以使用Scalaz 7.1OptionT monad转换器来编写flatMap

import scalaz._, Scalaz._

type F[A] = Option[A]

def flatMap[A, B](fa: Future[F[A]], f: A => Future[F[B]]): Future[F[B]] =
  OptionT(fa).flatMap(f andThen OptionT.apply).run

OptionT[Future, A]这是Future[Option[A]]的一种包装器。如果您的FList,则只需将OptionT替换为ListT,将run替换为underlying(依此类推)。

好消息是,例如,当您使用OptionT[Future, A]时,通常可以避免首先使用Future[Option[Future[Option[A]]]] - 请参阅我的回答here进行更详细的讨论。

一个缺点是并非所有的monad都有变压器。例如,您可以将Future放在堆栈的底部(正如我上面所做的那样),但这并不是定义FutureT的有用方法。

答案 1 :(得分:2)

这可能会回答“我想要一个实例:”部分。

$ scala
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> Future(List(Future(1),Future(2),Future(3))) // Future[F[Future[B]]]
res0: scala.concurrent.Future[List[scala.concurrent.Future[Int]]] = scala.concurrent.impl.Promise$DefaultPromise@41ab013

scala> res0.map(Future.sequence(_)) // transformed to Future[Future[F[B]]
res1: scala.concurrent.Future[scala.concurrent.Future[List[Int]]] = scala.concurrent.impl.Promise$DefaultPromise@26a4842b

scala> res1.flatMap(identity) // reduced to Future[F[B]]
res2: scala.concurrent.Future[List[Int]] = scala.concurrent.impl.Promise$DefaultPromise@4152d38d

希望下面的flatMap定义应该给出转换类型:)的想法 为了便于理解,我将F替换为List类型。

scala>   def flatMap[A, B](fa:Future[List[A]], f: A => Future[List[B]]):Future[List[B]] = {
     |     val x: Future[List[Future[List[B]]]] = fa.map(_.map(f))
     |     val y: Future[Future[List[List[B]]]] = x.map(Future.sequence(_))
     |     val z: Future[Future[List[B]]] = y.map(_.map(_.flatten))
     |     z.flatMap(identity)
     |   }
flatMap: [A, B](fa: scala.concurrent.Future[List[A]], f: A => scala.concurrent.Future[List[B]])scala.concurrent.Future[List[B]]