简体   繁体   English

Scala对未来和选项的理解

[英]Scala for comprehension with future and options

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
  }
}

I have two functions that return Future[Option[String]] and I need to combine the two strings into one string. 我有两个函数返回Future[Option[String]] ,我需要将两个字符串组合成一个字符串。

I want the output to be either combination of two strings and footer: "file one file two add more data to the file" or default when one or both of the Future s return None : "Files not found add more data to file". 我希望输出是两个字符串和页脚的组合:“文件一个文件两个向文件添加更多数据”或默认当一个或两个Future返回None :“找不到文件将更多数据添加到文件”。

How can this be achieved? 怎么能实现这一目标?

Compiler error: 编译错误:

Error:(16, 11) type mismatch;
found   : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
      ^ 

Well, without doing anything fancy like monad transformers or stuff, one can just simply nest for comprehensions. 好吧,如果不做像monad变形金刚或其他东西那样的任何东西,人们可以简单地for理解而筑巢。 It will be more wordy, but no extra dependencies. 它将更加冗长,但没有额外的依赖。

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))

Ultimately, the problem here is that you try to combine Future and Option in a single for comprehension. 最终,问题在于您尝试将FutureOption组合在一起for理解。 That just doesn't work for the reasons other respondents mentioned. 由于其他受访者提到的原因,这不起作用。 Nesting, however, works just fine. 但是,嵌套工作得很好。

The downside of the nesting is that you end up with very complex data structures, which might not be easy to use elsewhere in your program. 嵌套的缺点是最终会出现非常复杂的数据结构,这可能不容易在程序的其他地方使用。 You should think about how you would flatten them, ie going from Future[Option[String]] to just Future[String] . 你应该考虑如何压扁它们,即从Future[Option[String]]Future[String] Is your particular case you can do something like this: res.map(_.getOrElse("")) . 你的具体情况是这样的: res.map(_.getOrElse(""))

Okay, may 2 levels of nesting is fine, but you nest more than that, consider flattening that hierarchy before letting your colleagues deal with it. 好吧,可能有两个级别的嵌套很好,但是你嵌套的比较多,考虑在让同事处理它之前弄平该层次结构。 :) :)

Like alf mentioned in his answer, you can use monad tranformers for this, in this case OptionT . ALF在他的回答中提到,您可以使用单子发电产品对于这一点,在这种情况下OptionT

An example using 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")

Note that if you use cats, you can replace the for comprehension in combinedOT2 by using a cartesian builder (the |@| ), because file2 doesn't depend on file1 : 请注意,如果你使用的猫,可以更换为理解combinedOT2用笛卡尔生成器(该|@| ),因为file2不依赖于file1

val combinedOT2: Future[Option[String]] = 
  (OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value

You can still use fallbackTo if the "combined" Future fails, eventhough it is probably better to use recover or recoverWith to actually check which Throwable s you want to recover from. 如果“组合”的Future失败,你仍然可以使用fallbackTo ,尽管使用recoverrecoverWith来实际检查你想从哪个Throwable recover可能更好。

I think exactly this problem is covered in this 47deg blog post , as well as in this one : monads do not compose, so you need a transformer from one monad to another, as there's no flatMap operation that would (flat) map a Future into Option . 我认为这个问题在这篇47deg博文中以及在这篇 文章中都有所涉及: monads不构成,所以你需要从一个monad到另一个monad的变换器,因为没有flatMap操作可以(平面)将Future映射到Option

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM