Scala Future[Option[T]] 解包
Scala Future[Option[T]] Un Packing
在以下代码段中,
trait MyType1; trait MyType2
import scala.concurrent.Promise
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
我将 p1 和 p2 传递给另一个函数,在该函数中我使用成功的 Future 完成了 Promise。调用此函数后,我尝试读取 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 没有显示任何错误或任何错误,类型看起来一致。但我不确定为什么编译器不开心!有什么线索吗?
在 for 理解中,<-
右侧的表达式必须具有兼容的类型。这是因为 for 理解基本上是嵌套 flatMap
调用的语法糖。要解决这个问题,您可以将一个 for comprehension 嵌套在另一个 for comprehension 中,或者您可以在 Option
上使用 get
或 map
等方法。
另一种选择是使用 monad transformers,但这超出了本答案的范围。
你的 for comprehension 类型必须一致,所以你不能随意混合使用 Option
和 Future
。
在您的示例中,f1
returns 一个 Future[Option[MyType1]]
而 f2
returns 一个 Future[MyType2]
请记住,对于一系列 flatMap
/map
和潜在 withFilter
.
的理解脱糖
Future[A]
和 Option[A]
的 flatMap
的(简化)签名也是
def flatMap[B](f: A => Future[B]): Future[B]
def flatMap[B](f: A => Option[B]): Option[B]
for-comprehension脱糖的前两步
f1.flatMap { someF1Elem =>
someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]`
...
}
}
现在看到错误了吗?
现在,要解决此问题,您可以采用几种方法。一个非常方便的方法是使用 Monad Transformers,它允许您将(例如)Future
和 Option
组合成一个单一的 monad 类型 OptionT
.
具体可以从Future[Option[A]]
到OptionT[Future, A]
来回切换。基本思想是您可以在后者上进行 flatMap 并提取 A
.
类型的值
但在此之前,您需要先创建 "right shape" 类型,这样两者都是 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
在给定 Future[Option[A]]
的情况下构建 OptionT[Future, A]
,而 liftM[OptionT]
在 Future[A]
时实现相同的效果
本例中res
的类型是OptionT[Future, Unit]
,调用run
可以得到Future[Option[Unit]]
。
看"How does yield work",你的理解力相当于
f1.flatMap(someF1Elem: Option[MyType1] =>
someF1Elem.flatMap(f1Elem =>
f1Elem.map(f2Elem =>
"..."
)
)
)
基本上发生的事情是这样的:Future
上的 flatMap
被定义为采用 returns 另一个 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
个实例,则必须返回到 Future[Option[X]]
。正如 Kim 所建议的,您可以嵌套理解:
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"
}
}
您还可以在未定义选项 (NoSuchElementException
)
时使生成的 Future
失败
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"
}
}
在以下代码段中,
trait MyType1; trait MyType2
import scala.concurrent.Promise
val p1 = Promise[Option[MyType1]]()
val p2 = Promise[MyType2]()
我将 p1 和 p2 传递给另一个函数,在该函数中我使用成功的 Future 完成了 Promise。调用此函数后,我尝试读取 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 没有显示任何错误或任何错误,类型看起来一致。但我不确定为什么编译器不开心!有什么线索吗?
在 for 理解中,<-
右侧的表达式必须具有兼容的类型。这是因为 for 理解基本上是嵌套 flatMap
调用的语法糖。要解决这个问题,您可以将一个 for comprehension 嵌套在另一个 for comprehension 中,或者您可以在 Option
上使用 get
或 map
等方法。
另一种选择是使用 monad transformers,但这超出了本答案的范围。
你的 for comprehension 类型必须一致,所以你不能随意混合使用 Option
和 Future
。
在您的示例中,f1
returns 一个 Future[Option[MyType1]]
而 f2
returns 一个 Future[MyType2]
请记住,对于一系列 flatMap
/map
和潜在 withFilter
.
Future[A]
和 Option[A]
的 flatMap
的(简化)签名也是
def flatMap[B](f: A => Future[B]): Future[B]
def flatMap[B](f: A => Option[B]): Option[B]
for-comprehension脱糖的前两步
f1.flatMap { someF1Elem =>
someF1Elem.flatMap { f1Elem => // this returns a `Option[MyType1]`
...
}
}
现在看到错误了吗?
现在,要解决此问题,您可以采用几种方法。一个非常方便的方法是使用 Monad Transformers,它允许您将(例如)Future
和 Option
组合成一个单一的 monad 类型 OptionT
.
具体可以从Future[Option[A]]
到OptionT[Future, A]
来回切换。基本思想是您可以在后者上进行 flatMap 并提取 A
.
但在此之前,您需要先创建 "right shape" 类型,这样两者都是 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
在给定 Future[Option[A]]
的情况下构建 OptionT[Future, A]
,而 liftM[OptionT]
在 Future[A]
本例中res
的类型是OptionT[Future, Unit]
,调用run
可以得到Future[Option[Unit]]
。
看"How does yield work",你的理解力相当于
f1.flatMap(someF1Elem: Option[MyType1] =>
someF1Elem.flatMap(f1Elem =>
f1Elem.map(f2Elem =>
"..."
)
)
)
基本上发生的事情是这样的:Future
上的 flatMap
被定义为采用 returns 另一个 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
个实例,则必须返回到 Future[Option[X]]
。正如 Kim 所建议的,您可以嵌套理解:
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"
}
}
您还可以在未定义选项 (NoSuchElementException
)
Future
失败
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"
}
}