用于理解未来和选项的 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
}
}
我有两个函数 return Future[Option[String]]
并且我需要将两个字符串合并为一个字符串。
我希望输出是两个字符串和页脚的组合:"file one file two add more data to the file" 或 Future
中的一个或两个 return None
时的默认值:"Files not found add more data to file".
如何实现?
编译器错误:
Error:(16, 11) type mismatch;
found : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
^
我认为 this 47deg blog post, as well as in this one 中正好涵盖了这个问题:monads 不组合, 所以你需要一个从一个 monad 到另一个 monad 的转换器,因为没有 flatMap
将(平坦)映射 Future
到 Option
的操作。
就像他的回答中提到的 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")
请注意,如果您使用猫,则可以使用笛卡尔构建器(|@|
)替换 combinedOT2
中的 for comprehension,因为 file2
不依赖于 file1
:
val combinedOT2: Future[Option[String]] =
(OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value
如果 "combined" Future
失败,您仍然可以使用 fallbackTo
,尽管使用 recover
或 recoverWith
实际检查哪个可能更好Throwable
您想从中恢复。
好吧,不用做任何花哨的东西,比如 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(""))
。
好的,可能 2 层嵌套没问题,但您嵌套的不止于此,请考虑在让您的同事处理它之前先展平该层次结构。 :)
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
}
}
我有两个函数 return Future[Option[String]]
并且我需要将两个字符串合并为一个字符串。
我希望输出是两个字符串和页脚的组合:"file one file two add more data to the file" 或 Future
中的一个或两个 return None
时的默认值:"Files not found add more data to file".
如何实现?
编译器错误:
Error:(16, 11) type mismatch;
found : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
^
我认为 this 47deg blog post, as well as in this one 中正好涵盖了这个问题:monads 不组合, 所以你需要一个从一个 monad 到另一个 monad 的转换器,因为没有 flatMap
将(平坦)映射 Future
到 Option
的操作。
就像他的回答中提到的 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")
请注意,如果您使用猫,则可以使用笛卡尔构建器(|@|
)替换 combinedOT2
中的 for comprehension,因为 file2
不依赖于 file1
:
val combinedOT2: Future[Option[String]] =
(OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value
如果 "combined" Future
失败,您仍然可以使用 fallbackTo
,尽管使用 recover
或 recoverWith
实际检查哪个可能更好Throwable
您想从中恢复。
好吧,不用做任何花哨的东西,比如 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(""))
。
好的,可能 2 层嵌套没问题,但您嵌套的不止于此,请考虑在让您的同事处理它之前先展平该层次结构。 :)