Does for comprehension have something like “flatYield”?

一笑奈何 提交于 2019-12-23 14:55:21

问题


I have some code like

//all data have different types
val data1Future = loadData1(params)
val data2Future = loadData2(params)
val data3Future = loadData3(params)

def saveResult(rez): Future[_] = ???

data1Future.flatMap { data1 =>
  data2Future.flatMap { data2 =>
    data3Future.flatMap { data3 =>
      //do some computation
      //several rows and several vals
      val rez = ???
      saveResult(rez)
   }
  }
}

But it is a litle bit ugly :) Unfortunatelly, I can't use for comprehension since I need something like "flatYield"

for {
  data1 <- data1Future
  data1 <- data1Future
  data1 <- data1Future
} flatYield {
  //do some computation
  //several rows and several vals
  val rez = data1 + data2 + data3
  saveResult(rez)
}

Do you know pattern that is such elegant as "for comprehension" but with flatMap instead of map at the end of the chain?


回答1:


You can do this:

for {
  data1 <- data1Future
  data2 <- data2Future
  data3 <- data3Future
  rez = {
    //do some computation
    //several rows and several vals
    data1 + data2 + data3
  }
  r <- saveResult(rez)
} yield r

This translates to

data1Future.flatMap { data1 =>
  data2Future.flatMap { data2 =>
    data3Future.flatMap { data3 =>
      val rez = {
        //do some computation
        //several rows and several vals
        data1 + data2 + data3
      }
      saveResult(rez).map(r => r)
    }
  }
}

which is isomorphic to your code.




回答2:


It seems like you just want another line within your for-comprehension, and all of that "other computation" should go within another function to keep things clean:

for {
  data1 <- data1Future
  data2 <- data2Future
  data3 <- data3Future
  rez <- otherFunction(data1, data2, data3)
} yield rez 

def otherFunction(d1: ?, d2: ?, d3: ?): Future[?] = {
  //do some computation
  //several rows and several vals
}

Alternatively you can use something like this:

(for {
  data1 <- data1Future
  data2 <- data2Future
  data3 <- data3Future
} yield {
  (data1, data2, data3)
}) flatMap { case (data1, data2, data3) =>
  //do some computation
  //several rows and several vals
  saveResult(rez)
}



回答3:


Using a for comprehension (like your stated goal) is equivalent to the defining the futures in a sequence of flatMap operations will wait for the result of each future before going to the next. Since your futures don't depend on each other you can start them all without waiting for the previous one to complete - this is what you have done in your initial example.

Start them concurrently and store the futures in a sequence use Future.sequence to turn the collection of futures into a single Future that won't complete until ALL of them complete (or any one of them fails). Then save your result when the future Completes.

val data1Future = loadData1(params) //type = Future[foo]
val data2Future = loadData2(params)
val data3Future = loadData3(params)

val listOfFutures = List(data1Future,data2Future,data3Future) //type = List[Future[foo]]

val futureOfList = Future.sequence(listOfFutures)  //type = Future[List[foo]]

futureOfList.onComplete( list => saveResult(list.reduce(_ + _))


来源:https://stackoverflow.com/questions/26913693/does-for-comprehension-have-something-like-flatyield

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!