object Main extends App {
val p1 = Promise[Option[String]]()
val p2 = Promise[Option[String]]()
val f1 = p1.future
val f2 = p2.future
val res = (for{
file1Opt <- f1
file2Opt <- f2
file1 <- file1Opt
file2 <- file2Opt
} yield {
combineFiles(file1, file2)
}).fallbackTo(Future.successful("Files not found"))
Thread.sleep(2000)
println("XXXXXXXXXXXXXXXXXXX")
p1.success(Some("file one"))
p2.success(Some("file two"))
val finalData = res.map(s =>
s + " " + "add more data to the file"
)
finalData.map(println(_))
def combineFiles(f1: String, f2: String): String = {
f1 + " " + f2
}
}
我有两个返回Future[Option[String]]
的函数,我需要将两个字符串合并为一个字符串。
我希望输出是两个字符串和页脚的组合:“文件一个文件两个向文件添加更多数据”或默认当Future
中的一个或两个返回None
时: “找不到文件会向文件添加更多数据”。
如何实现这一目标?
编译错误:
Error:(16, 11) type mismatch;
found : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
^
答案 0 :(得分:8)
嗯,没有像monad变形金刚或其他东西那样花哨的东西,人们可以简单地嵌套for
理解。它会更加冗长,但没有额外的依赖。
val res = (for{
file1Opt <- f1
file2Opt <- f2
} yield for {
file1 <- file1Opt
file2 <- file2Opt
} yield combineFiles(file1, file2))
.fallbackTo(Future.successful(Some("Files not found")))
//or, alternatively, .fallbackTo(Future.successful(None))
最终,问题在于您尝试将Future
和Option
合并为一个for
理解。由于其他受访者提到的原因,这种做法并不起作用。但是,嵌套工作得很好。
嵌套的缺点是最终会出现非常复杂的数据结构,这可能不容易在程序的其他地方使用。你应该考虑如何压扁它们,即从Future[Option[String]]
到Future[String]
。在特殊情况下,您可以执行以下操作:res.map(_.getOrElse(""))
。
好吧,可能有两个级别的嵌套很好,但是你嵌套的比较多,考虑在让同事处理它之前弄平该层次结构。 :)
答案 1 :(得分:5)
与他的回答中提到的 alf 一样,你可以使用monad变换器,在这种情况下OptionT
。
使用cats的示例:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.OptionT
import cats.implicits._
val file1: Future[Option[String]] = Future.successful(Some("file1"))
val file2: Future[Option[String]] = Future.successful(Some("file2"))
val combinedOT: OptionT[Future, String] =
for {
f1 <- OptionT(file1)
f2 <- OptionT(file2)
} yield s"$f1 $f2"
val combinedFO: Future[Option[String]] = combinedOT.value
val combinedF: Future[String] = combinedOT.getOrElse("Files not found")
请注意,如果您使用cat,则可以使用笛卡尔构建器(combinedOT2
)替换|@|
中的for comprehension,因为file2
并不依赖{ {1}}:
file1
如果&#34;合并&#34;您仍然可以使用val combinedOT2: Future[Option[String]] =
(OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value
fallbackTo
失败,尽管最好使用Future
或recover
来实际检查您要恢复的recoverWith
。
答案 2 :(得分:1)
我认为this 47deg blog post以及this one中都包含了这个问题: monad不构成,所以你需要从一个monad变换到另一个monad,因为没有flatMap
操作可以(平面)将Future
映射到Option
。