类型化函数式编程中的模式 Option[Future[Int]] 转换为 Future[Option[Int]]

问题描述 投票:0回答:3

我发现自己在使用 Scala 时遇到的一个常见情况是以下流程:

  1. 阅读一些返回
    Option
  2. 的 props
  3. Future
  4. 中使用道具
  5. 对未来的结果进行一些转换,返回
    Option

这会产生诸如

Option[Future[Option[_]]]
之类的类型,更好的解决方案是使用诸如
Future[Option[Option[_]]]
之类的转换,例如使用如下所示的内容:

def transform[A](o: Option[Future[A]]): Future[Option[A]] =
  o.map(f => f.map(Option(_))).getOrElse(Future.successful(None))

(代码从这里被盗)

然后我可以使用

Options
Future
中处理任意数量的
flatmap

这似乎是一种常见的模式,我确信它可以在 Scala 中以某种惯用的方式使用,而无需我一遍又一遍地实现转换方法。

所以我的问题是:像上面的例子一样,将

Option[Future[Option[_]]]
从外转入最惯用的方法是什么?

scala monads monad-transformers
3个回答
1
投票

cats 库中的

Traverse
类型类可能会有所帮助。它可以处理将
Option[Future[Something]]
转换为
Future[Option[Something]]
的样板。

使用 Ammonite REPL 的示例:

$ amm
Loading...
Welcome to the Ammonite Repl 0.7.7
(Scala 2.11.8 Java 1.8.0_101)
@ import $ivy.`org.typelevel::cats-core:0.7.2`
import $ivy.$

首先是一些进口...

@ import cats.Traverse
import cats.Traverse
@ import cats.implicits._
import cats.implicits._
@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
@ import scala.concurrent.Future
import scala.concurrent.Future

这是我们要改变的东西:

@ val optionOfFuture: Option[Future[String]] = Some(Future.successful("hello"))
optionOfFuture: Option[Future[String]] = Some(scala.concurrent.impl.Promise$KeptPromise@43ac3b45)

我们使用

Traverse
sequence
方法来切换
Option
Future

@ val futureOfOption: Future[Option[String]] = Traverse[Option].sequence(optionOfFuture)
futureOfOption: Future[Option[String]] = Success(Some(hello))

或者如果您更喜欢语法糖版本:

@ import cats.syntax.traverse._
import cats.syntax.traverse._
@ val futureOfOption2: Future[Option[String]] = optionOfFuture.sequence
futureOfOption2: Future[Option[String]] = Success(Some(hello))

有关

Traverse
还能做什么的更多信息,请查看 cats 文档


1
投票

我认为,你的问题在于问题中的#3:为什么“对结果进行一些转换”,操纵

Future
返回
Option
?那味道不对。只要让他们首先返回
Future
,你就不会遇到这个问题了。

顺便说一句,我不确定“能够使用 flatMap 处理任意数量的选项”到底是什么意思,但这几乎肯定是错误的:

flatMap
将帮助你摆脱 1额外级别的选项在地图时
Some(Some("foo")).flatMap(x => Some(s))
产生
Some(Some("foo"))
.flatten
明确地做了同样的事情:
Some(Some(Some("foo"))).flatten
产生
Some(Some("foo"))
,而不是您可能期望的
Some("foo")
"foo"

请注意,在每种情况下,扁平化处理的选项只有一层,而不是“任意数量”。 通常的方法是在遇到额外选项时将其删除(立即展平以始终拥有

Future[Option[T]]
而不是
Future[Option[Option[T]]]
)。


0
投票
val a: Option[Future[Option[T]]] = ...

val b: Future[Option[T]] = a.getOrElse(Future(None))
© www.soinside.com 2019 - 2024. All rights reserved.