18

Suppose I want to aggregate data from 2 remote services, and serve response as fast as I can:

def loadUser: Future[User]
def loadData: Future[Data]

case class Payload(user: User, data: Data)

I understand that this one executes async tasks sequentially:

for {
  user <- loadUser
  data <- loadData
} yield Payload(user,data)

While this one executes them in parallel because async tasks are triggered before being sequentially chained:

val userF = loadUser
val dataF = loadData
for {
  user <- userF 
  data <- dataF
} yield Payload(user,data)

The difference is however a bit too implicit for me and someone may not notice it at first.


Applicatives also solves the job

(loadUser |@| loadData) { Payload(_,_) }

Can someone tell me what I'd rather use between applicatives and monads to perform parallel async computation? What are the pros and cons of each approach?

4
  • 3
    I think that fundamentally if you need the output of someComputationA in order to compute the output of someComputationB you use a monad. If you just want to compute two separate things and combine them, applicative will do. While this is nothing specifically 'parallel' about applicatives, I would argue that for the reasons given above, monads are inherently sequential. There for if you need truly parallel computations, monads are probably not the structure you are looking for. waves hands
    – melps
    Commented Feb 1, 2016 at 23:33
  • 5
    My answer here has some relevant discussion. Commented Feb 2, 2016 at 0:17
  • Standard idiom for running Future computations in parallel is zip: for ((user, data) <- loadUser zip loadData) yield Payload(user, data)
    – Kolmar
    Commented Feb 2, 2016 at 6:15
  • You can probably find this discussion useful.
    – Michael
    Commented Feb 2, 2016 at 7:24

1 Answer 1

15

So, I'm answering my own question because all comments link to useful resources.

Travis Brown had a nice answer:

it's just a solid development practice to use the least powerful abstraction that will get the job done. In principle this may allow optimizations that wouldn't otherwise be possible, but more importantly it makes the code we write more reusable.

Also he points out an interesting fact:

It's a shame that both Haskell and Scala currently make working with monads so much more convenient (syntactically, etc.) than working with applicative functors

Kolmar pointed out that it's possible to zip 2 futures:

for ((user, data) <- loadUser zip loadData) yield Payload(user, data)

However it seems that zipping more than 2 futures is not so elegant.

So it seems that Applicative functor is best suited for the job, but the Scala standart library does not encourage us much to use them compared to monad, and you need an extra library like Scalaz or Cats

Not the answer you're looking for? Browse other questions tagged or ask your own question.