当前位置: 首页 > 知识库问答 >
问题:

用于理解的Scala中的可遍历一次、未来和选项

井轶
2023-03-14

我有一个表示DB记录的字符串id列表。我想从数据库异步加载它们,然后异步地将每个记录上传到远程服务器,然后当所有的上传都完成时,记录已上传记录的id。

因为我使用的是Scala 2.9.2,所以我使用的是Twitter的core-util Future实现,但是在单子转换方面,它应该和2.10 futures完全一样。

总的概念是这样的:

def fetch(id: String): Future[Option[Record]]
def upload(record: Record): Future[String]
def notifyUploaded(ids: Seq[String]): Unit

val ids: Seq[String] = ....

为了便于理解,我尝试通过一个来实现,但是fetch返回一个Future of Option的事实使它变得模糊不清,代码无法编译:

for {
  id <- ids
  maybeRecord <- fetch(id)
  record <- maybeRecord
  uploadedId <- upload(record)
} yield uploadedId

编译此文件会导致以下错误:

scala: type mismatch;
found   : com.twitter.util.Future[String]
required: Option[?]
    uploadedId <- upload(record)
                  ^

我错过了什么?为什么编译器期望上传ID是一个选项?有什么好的方法可以解决这个问题吗?

共有2个答案

赖俊
2023-03-14

你不能将所有不同的类型混合在一起进行理解,我发现你可以混合Seq和Option,结果将是Seq或Option,具体取决于第一个是什么。不可能混合使用Future和Seq或Option。如果你想使用理解,你必须将它们级联在一起。在这种情况下,使用map/flatMap可能会更好。我以两种方式实现了您的问题,并将类型添加到几个中间结果中,以便您在使用所有不同类型的类型时看到正在创建的混乱。

object TestClass {

  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent._
  import scala.concurrent.duration._

  case class Record(id: String)


  def fetch(id: String): Future[Option[Record]] = Future {
    Thread.sleep(1000);
    Some(Record(id))
  }

  def upload(record: Record): Future[String] = Future {
    Thread.sleep(3000);
    record.id + "_uploaded"
  }

  def notifyUploaded(ids: Seq[String]): Unit = println("notified" + ids)

  val ids: Seq[String] = Seq("a", "b", "c")

  def main(args: Array[String]): Unit = {
    forComprehensionImpl()
    mapAndFlatMapImpl()
  }

  def forComprehensionImpl() = {
    val result: Seq[Future[Option[Future[String]]]] = for {
      id <- ids
    } yield {
      for {
        maybeRecord <- fetch(id)
      } yield {
        for {
          record <- maybeRecord
        } yield {
          for {
            uploadedId <- upload(record)
          } yield uploadedId
        }
      }
    }
    val result2: Future[Seq[Option[Future[String]]]] = Future.sequence(result)
    val result3: Future[Unit] = result2.flatMap { x: Seq[Option[Future[String]]] =>
      Future.sequence(x.flatten).map(notifyUploaded)
    }
    Await.result(result3, Duration.Inf)
  }


  def mapAndFlatMapImpl() = {
    val res: Seq[Future[Iterable[String]]] = ids.map { id =>
      fetch(id).flatMap { maybeRecord =>
        val res1: Option[Future[Seq[String]]] = maybeRecord.map { record =>
          upload(record) map (Seq(_))
        }
        res1 match {
          case Some(a) => a
          case None => Future(Seq())
        }
      }
    }
    val res3: Future[Unit] = Future.sequence(res) map (a => notifyUploaded(a.flatten))
    Await.result(res3, Duration.Inf)
  }
}
姜玮
2023-03-14

考虑 flatMap(或 bind)函数的签名:

trait Monad[M[_]] {
  def flatMap[A](a : M[A], f : A => M[B]) : M[B]
  ....

在您的例子中,您试图对< code>Option使用< code>flatMap,赋予它一个生成< code>Future的< code>f。但是在上面的签名中,< code>f应该在调用它的同一个单子中生成一些东西。

Scala在这方面不一定非常有用,因为它非常擅长转换(例如转换为<code>Seq</code>s),让人觉得可以将任意<code>flatMap</code>调用链接在一起,而不管容器是什么。

你可能想要的是一个“单子转换器”,它给你一些能力来组成单子。Debasish Ghosh在这里有一个关于使用Scalaz monad变形金刚的帖子。

 类似资料:
  • 我有两个函数返回期货。我正试图使用for-yield理解将第一个函数的修改结果输入到另一个函数中。 此方法有效: 然而,我对“如果”在那里不满意,似乎我应该能够使用地图。 但是当我尝试使用地图时: 我得到一个编译错误: 我尝试了一些变化,但没有发现任何有吸引力的工作。有人能提出更好的理解和/或解释我的第二个例子的错误吗? 下面是一个最小但完整的Scala 2.10可运行示例:

  • 任务是实现递归方法,返回 Future 并且由于这一部分("在其他递归(结果::: res,尝试1)")代码失败与错误,因为它期望未来[Seq[结果]],但实际上返回未来[对象]。 据我所知,问题在于yield块内的表达式必须返回Seq[Result],以便将来由Monad进行后续包装。但是“递归(结果::res,尝试1)”将返回未来。因此,不是预期的Seq[Result]收益率,而是包含未来的[

  • 我正在阅读Scala Cookbook(http://shop.oreilly.com/product/0636920026914.do) 有一个与未来使用相关的例子,涉及理解。 到目前为止,我对理解的理解是,当与一个集合一起使用时,它会产生另一个相同类型的集合。例如,如果每个< code>futureX的类型为< code>Future[Int],则以下内容也应为< code>Future[In

  • Scala使用什么模式来处理这种情况: 你有很多未来(它们可以是任何东西,但为了举例…) 你有一个返回未来的函数 我想做这样的事情: 我想返回一个值,但是我在rent语句中调用foF,我会得到一个

  • 有没有可能实现这样的事情: 我希望清楚我的意图是什么。

  • 我想在我的play scala Web应用程序中进行错误处理。 我的应用程序与数据库对话以获取一些行,它遵循以下流程。 < li >首先调用数据库以获取一些数据 < li >使用第一次调用中的数据从数据库中提取其他数据 < li >使用从最近两次db调用中收到的数据形成响应。 下面是我的伪代码。 以上理解中的每一个方法都返回一个未来,这些方法的签名如下。 在以下情况下,我该如何进行错误/故障处理