ocwirk
ocwirk

Reputation: 1089

Flatten long nesting of Monadic types in Scala

I have a function to get a seq of workItemIds given JobIds, with following signature -

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

I have another function which given a WorkItemId would return a workItem. with signature -

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

Now I am trying to write a function which uses these two to return a seq of WorkItem given a JobId, like this -

  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
}

The problem is the return type, I don't want to return this monstrous type, instead something simpler like a Future[Seq[WorkItem]] should be returned. Now I can flatten it like -

  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
}

which gives me the correct Future[Seq[WorkItem]] type that I want, but it requires me to do a get on the future, which does not feel correct. I can also use await but that would be a blocking call. Is there anyway to flatten the above type without blocking ?

Upvotes: 1

Views: 94

Answers (2)

Nyavro
Nyavro

Reputation: 8866

You can do it the following way:

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

Upvotes: 3

Shadowlands
Shadowlands

Reputation: 15074

What you are looking for is Future.traverse:

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

The .traverse call takes the Seq from the getWorkItemIds call and returns a Future[Seq] of the results of calling getWorkItem on each entry, without the inner Future wrapping.

The .map(_.flatten) near the end flattens out this inner Seq[Option[WorkItem]] to just Seq[WorkItem].

Upvotes: 3

Related Questions