在 Scala 中展平 Monadic 类型的长嵌套

Flatten long nesting of Monadic types in Scala

我有一个函数可以在给定 JobId 的情况下获取一系列 workItemId,并具有以下签名 -

def getWorkItemIds(jobId: JobId): Future[Seq[WorkItemId]]

我有另一个函数,给定一个 WorkItemId 将 return 一个 workItem。有签名 -

def getWorkItem(workItemId: WorkItemId): Future[Option[WorkItem]]

现在我正在尝试编写一个函数,它使用这两个函数来 return 给定 JobId 的 WorkItem 序列,就像这样 -

  def getWorkItemsForJob(jobId: JobId): Future[Seq[Future[Option[WorkItem]]]] = {
val workItemIds: Future[Seq[WorkItemId]] = getWorkItemIds(jobId)

   val res = workItemIds.map {
     idSeq => idSeq.map {
       id => getWorkItem(id)
     }
   }
   res
}

问题是 return 类型,我不想 return 这种可怕的类型,而应该 return 像 Future[Seq[WorkItem]] 这样更简单的类型。现在我可以把它压平了 -

  def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] = {
val workItemIds: Future[Seq[WorkItemId]] = getWorkItemIds(jobId)

  val res = workItemIds.map {
    idSeq => idSeq.flatMap {
      id => getWorkItem(id).get
    }
  }
  res
}

这给了我我想要的正确 Future[Seq[WorkItem]] 类型,但它要求我对未来做一个 get,这感觉不正确。我也可以使用 await 但这将是一个阻塞调用。有没有办法在不阻塞的情况下展平上述类型?

你要找的是Future.traverse:

def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] =
  getWorkItemIds(jobId).flatMap(Future.traverse(_)(getWorkItem).map(_.flatten)) 

.traverse 调用获取 getWorkItemIds 调用的 Seq 和 returns 调用 getWorkItem 结果的 Future[Seq]每个条目,没有内部 Future 包装。

最后的 .map(_.flatten) 将内部 Seq[Option[WorkItem]] 变平为 Seq[WorkItem]

您可以通过以下方式进行:

def getWorkItemsForJob(jobId: JobId): Future[Seq[WorkItem]] = 
  for (
    seq <- getWorkItemIds(jobId);
    list <- Future.traverse(seq)(getWorkItem)
  ) yield list.flatten