0

I have a code which is using for-comprehension to run database query :

val totalFeeNoticeAmountFromDB = Future(/..Doing db job../)(executionContext)
val listOfRestrictedFundFromDB = Future(/..Doing db job../)(executionContext)

val res = for {
      totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
      listOfRestrictedFund <- listOfRestrictedFundFromDB
    } yield (totalFeeNoticeAmount, listOfRestrictedFund) 

We know for running for-comprehension we need to pass implicit execution context. But in this case I am wanting to pass execution context manually.
What is the way ?

Edited:

val res = for {
          totalFeeNoticeAmount <-(?:ExecutionContext) totalFeeNoticeAmountFromDB
          listOfRestrictedFund <-(?:ExecutionContext) listOfRestrictedFundFromDB
        } yield (totalFeeNoticeAmount, listOfRestrictedFund) 

totalFeeNoticeAmountFromDB and listOfRestrictedFundFromDB are both Future type already initiated.

Is there any way of passing here <-(?:ExecutionContext) ?

John
  • 2,633
  • 4
  • 19
  • 34
  • Any implicit argument can be passed explicitly as a plain argument, but if there are multiple implicit arguments for the same call, then all these arguments must be passed explicitly. – cchantep Jun 28 '20 at 12:00
  • @cchantep, I know that. I edited my question. Actually I am wanting to pass execution context in this `<-(?:ExecutionContext)` part explicitly. – John Jun 28 '20 at 13:33
  • 1
    I believe the simplest way would be to create a new block or function for that part and set a new implicit execution context for that block. – Luis Miguel Mejía Suárez Jun 28 '20 at 13:59
  • Other simple alternative, given your futures do not depend on each other would be to create them with the explicit EC you want and then compose them. – Luis Miguel Mejía Suárez Jun 28 '20 at 14:10

3 Answers3

6

Perhaps consider scala-async which has gained experimental compiler support -Xasync in Scala 2.13.3 where the following for-comprehension

for {
  a <- Future(41)
  b <- Future(1)
} yield {
  a + b
}

can be rewritten as

async {
  val a = async(41)(ec)
  val b = async(1)(ec)
  await(a) + await(b)
}(ec)

where we can pass in execution context ec explicitly without resorting to flatMap/map.

Another hacky option could be better-monadic-for which supports defining implicits inside for-comprehensions

val ec: ExecutionContext = ???
(for {
  implicit0(ec: ExecutionContext) <- Future.successful(ec)
  a <- Future(41)(ec)
  b <- Future(1)(ec)
} yield {
  a + b
})(ec)
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • `for { a <-(?:ExecutionContex) Future(41)(ec) b <-(?:ExecutionContex) Future(1)(ec) } yield { a + b }` I want to pass in this `<-(?:ExecutionContex)` – John Jun 28 '20 at 13:17
  • in that case I will need "scala-async" lib. I am up-voting you. But is there any way in `for-comprehension` ? – John Jun 28 '20 at 14:07
  • 1
    @MHJ I believe not, see https://stackoverflow.com/a/21256762/5205022 – Mario Galic Jun 28 '20 at 14:39
3

You can rewrite

val res = for {
  totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
  listOfRestrictedFund <- listOfRestrictedFundFromDB
} yield (totalFeeNoticeAmount, listOfRestrictedFund)

as

val res = totalFeeNoticeAmountFromDB.flatMap(totalFeeNoticeAmount =>
  listOfRestrictedFundFromDB.map(listOfRestrictedFund =>
    (totalFeeNoticeAmount, listOfRestrictedFund)
  )
)

For example if totalFeeNoticeAmountFromDB and listOfRestrictedFundFromDB are Futures then you can pass implicit scala.concurrent.ExecutionContext.Implicits.global explicitly

val res = totalFeeNoticeAmountFromDB.flatMap(totalFeeNoticeAmount =>
  listOfRestrictedFundFromDB.map(listOfRestrictedFund =>
    (totalFeeNoticeAmount, listOfRestrictedFund)
  )(scala.concurrent.ExecutionContext.Implicits.global)
)(scala.concurrent.ExecutionContext.Implicits.global)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • 1
    @MHJ for comprension is is always sequential not parallel. – Luis Miguel Mejía Suárez Jun 28 '20 at 13:58
  • 1
    @MHJ no it doesn't matter if it relies in the previous or not. It is always sequential because for comprehension is just sugar syntax for `flatMap` so it is the same code. You can make it run like in parallel if you define the futures before before the `for`, because futures are eager they will be running before sequencing them, but that is actually a bug and not a feature. Anyways, you can do exactly the same with `flatMap` – Luis Miguel Mejía Suárez Jun 28 '20 at 14:07
  • @LuisMiguelMejíaSuárez, yes you are right. Thing is if we initiate the future before we call in for-comprehension , parallel things happens before coming to for-comprehension – John Jun 28 '20 at 14:15
  • 1
    @MHJ yes and I am telling you you can do exactly the same using `flatMap` so that should solve your problem. – Luis Miguel Mejía Suárez Jun 28 '20 at 14:19
  • @LuisMiguelMejíaSuárez, yes by flatMap I can solve that way and in parallel if I initiate the futures before come to flatMap. My problem was using explicit `executionContext in <-(?:ExecutionContext)` – John Jun 28 '20 at 14:24
  • @MHJ ok so let me put this simple. There is no way to pass explicitly the execution context used in a **Future** `for comprehension` period. You have a couple of alternatives: 1. Using `flatMap` instead so you can define the ec used in all operations. - 2. Encapsulate all this on it own function, so you can pass the EC for the whole function. – Luis Miguel Mejía Suárez Jun 28 '20 at 14:38
  • 2
    @MHJ Well, you can create your own compiler plugin (like "better-monadic-for" plugin mentioned by **Mario Galic** in his answer) that will expand `for { a <-(ec) fa; b <-(ec1) fb } yield (a, b)` into `fa.flatMap(a => fb.map(b => (a, b))(ec))(ec1)` but this would be an overkill. Such plugin will need to modify `parser` phase as well because `for { a <-(ec) fa...` isn't parsable now ("better-monadic-for" doesn't modify `parser`). – Dmytro Mitin Jun 28 '20 at 14:58
2

I believe the simplest solution to this problem is just to create an auxiliary function.

def foo(implicit ec: ExecutionContext): Future[(Int, Int)] = {
  val totalFeeNoticeAmountFromDB = Future(/..Doing db job../)
  val listOfRestrictedFundFromDB = Future(/..Doing db job../)

  for {
    totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
    listOfRestrictedFund <- listOfRestrictedFundFromDB
  } yield (totalFeeNoticeAmount, listOfRestrictedFund)
}

That way when you need it you can just call it like this: foo(ec = myExplicitExecutionContext)