2015-07-20 58 views
5

在下面的代码段中,Scala的未来[选项[T]]联合国包装

trait MyType1; trait MyType2 
import scala.concurrent.Promise 

val p1 = Promise[Option[MyType1]]() 
val p2 = Promise[MyType2]() 

我通过在p1和p2到另一个函数,其中我使用一个成功的未来完成的前景。呼叫此功能后,我尝试读取Promise中的值:

trait Test { 
    // get the Future from the promise 
    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem <- f1 
    f1Elem  <- someF1Elem 
    f2Elem  <- f1Elem 
    } yield { 
    // do something with f1Elem and f2Elem 
    "..." 
    } 
} 

当我尝试编译此问题时,出现一些编译器问题。

Error:(52, 19) type mismatch; 
found : Option[Nothing] 
required: scala.concurrent.Future[?] 
     flElem  <- someF1Elem 
       ^

IntelliJ显示没有错误或者什么都没有,并且类型看起来是一致的。但我不确定编译器为什么不快乐!任何线索?

回答

15

您的理解类型必须一致,因此您不能像您那样自由混合OptionFuture

在你的榜样,f1返回Future[Option[MyType1]]f2回报Future[MyType2]

请记住,对于修真desugars一系列的flatMap/map和潜在withFilter

同样的flatMapFuture[A]Option[A](简化)签名

def flatMap[B](f: A => Future[B]): Future[B] 
def flatMap[B](f: A => Option[B]): Option[B] 

前两个步骤进行,理解desugar来

f1.flatMap { someF1Elem => 
    someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]` 
    ... 
    } 
} 

看到的错误呢?


现在,要解决这个问题,有几种方法可以遵循。一个非常方便的是使用Monad变形金刚,它允许您将(例如)FutureOption组合成一个单一的monad类型OptionT

具体而言,您可以从Future[Option[A]]来回OptionT[Future, A]。基本思想是你可以对后者进行flatmap并提取类型为A的值。

但在这之前,你需要让你的类型的“正确的形状”的,所以两者都是一个Future[Option[Something]]

下面是一个使用scalaz一个例子

import scalaz._; import Scalaz._ ; import scalaz.OptionT._ 
import scala.concurrent.{ Promise, Future } 
import scala.concurrent.ExecutionContext.Implicits.global 

trait MyType1 
trait MyType2 

val p1 = Promise[Option[MyType1]]() 
val p2 = Promise[MyType2]() 

val f1 = p1.future 
val f2 = p2.future 

val res = for { 
    f1Elem <- optionT(f1) 
    f2Elem <- f2.liftM[OptionT] 
} yield { 
    println(s"$f1Elem $f2Elem") 
} 

optionT构建一个OptionT[Future, A]给予Future[Option[A]] ,而liftM[OptionT]达到相同时,你有一个Future[A]

res的类型是OptionT[Future, Unit]在这种情况下,你可以通过调用run得到Future[Option[Unit]]。在"How does yield work"

2

为了便于理解,<-右侧的表达式必须具有兼容的类型。这是因为对于嵌套flatMap调用来说,理解基本上是语法糖。为了解决这个问题,你可以有理解嵌套在另一个理解,或者你可以在Option上使用像getmap这样的方法。

另一种选择是使用monad transformers,但这超出了本答案的范围。

4

看,你的理解是相当于

f1.flatMap(someF1Elem: Option[MyType1] => 
    someF1Elem.flatMap(f1Elem => 
    f1Elem.map(f2Elem => 
     "..." 
    ) 
) 
) 

基本上会发生什么情况是这样的:在一个FutureflatMap被定义为采取返回另一个Future的功能。 这给你一个类似的错误:如果你想操作异步执行,即真正映射Future实例

<console>:64: error: value map is not a member of MyType1 f1Elem.map(f2Elem => ^ <console>:63: error: type mismatch; found : Option[Nothing] required: scala.concurrent.Future[?] someF1Elem.flatMap(f1Elem => ^

,你将不得不回到一个Future[Option[X]]。作为金建议,你可以嵌套的理解:

import scala.concurrent.{Future, Promise} 

def test: Future[Option[String]] = { 
    val p1 = Promise[Option[MyType1]]() 
    val p2 = Promise[MyType2]() 
    // ... run code that completes the promises 

    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem: Option[MyType1] <- f1 
    f2Elem  <- f2 
    } yield { 
    for (f1Elem <- someF1Elem) yield "something" 
    } 
} 

你也可以让没有定义选项时所产生的Future失败(NoSuchElementException

def test: Future[String] = { 
    val p1 = Promise[Option[MyType1]]() 
    val p2 = Promise[MyType2]() 
    // ... run code that completes the promises 

    val f1 = p1.future 
    val f2 = p2.future 

    for { 
    someF1Elem: Option[MyType1] <- f1 
    f2Elem <- f2 
    } yield { 
    val f1Elem = someF1Elem.get // may cause an exception and fail the future 
    "something" 
    } 
}