我不确定如何描述这个问题,所以我只会显示类型签名。
我有以下实例:
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]] = ???
也许这在概念上是一件奇怪的事情。欢迎任何关于有用抽象的提示。
答案 0 :(得分:7)
正如Rex Kerr上面所说,你经常可以使用monad变换器来处理你发现自己有像这样的交替层的情况。例如,如果此处F
为Option
,您可以使用Scalaz 7.1的OptionT
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]]
的一种包装器。如果您的F
为List
,则只需将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]]