4

I have the following code in Scala:

case class Water(temp: Int)

case class Milk(temp: Int)

def heatWaterFor(minutes: Int, water: Water) = Future {
  Thread.sleep(1000)
  Water(82)
}

def boilMilkFor(minutes: Int, milk: Milk) = Future {
  Thread.sleep(1000)
  Milk(90)
}

def frothMilk(hotwater: Water, hotmilk: Milk) = Future {
  Thread.sleep(1000)
  hotmilk
}

val start = System.currentTimeMillis()

val milkMaker = for {
   water <- heatWaterFor(10, Water(10))
   milk <- boilMilkFor(5, Milk(10))
   frothed = frothMilk(water, milk)
   hotMilk <- frothed
 } yield (hotMilk)

Await.ready(milkMaker, Duration.Inf)
val end = System.currentTimeMillis() - start
println(milkMaker.value + " , Time taken: "+((end/1000))+" seconds.")

My intent here is to parallelize heatWaterFor(...) and boilMilkFor(...) as they are independent. But I feel that the above code is sequential and doesn't harness the power of futures at all. Apparently, it takes 3000 milliseconds to run (which is an additional proof).

What is the fundamental thing I'm missing here?

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
user1189332
  • 1,773
  • 4
  • 26
  • 46

3 Answers3

3

A for expression is reduced to a series of map, flatMap and withFilter operations. Your specific for expression is reduced to something like this:

heatWaterFor(10, Water(10))
        .flatMap(water => boilMilkFor(5, Milk(10))
                             .flatMap(milk => frothMilk(water, milk))

As you can see here, the execution of the next future is started when the previous finished. Therefore, if you want to execute them all in parallel, you would need to do something like this:

val heatWater = heatWaterFor(10, Water(10))
val boilMilk = boilMilkFor(5, Milk(10))

val milkMaker = for {
   water <- heatWater
   milk <- boilMilk
   hotMilk <- frothMilk(water, milk)
} yield (hotMilk)

This will start heatWaterFor and boilMilkFor concurrently, and then start frothMilk when both of these are done (because it depends on the result of the other two futures).

marstran
  • 26,413
  • 5
  • 61
  • 67
1

I'm guessing that you are following: http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html . Towards the end it mentions why this piece of code is sequential and how to parallelize it (Last code snippet).

markiz
  • 265
  • 1
  • 7
  • Would you mind posting the relevant code here? Link-only answers are frowned upon in SO. – dcastro Aug 27 '15 at 07:43
  • Point taken, however marstran has already posted the code in his answer so I'll refrain from posting it again. – markiz Aug 27 '15 at 09:18
1

Check this answer.

In short, when you use for like this, each of your variables (water, milk, frothed) start to evaluate when previous are ready. This allows to use the results of previous evaluations in later ones, but it makes computation sequential.

Also (unrelated) your methods that create futures (heatWaterFor, etc.) don't use arguments.

Community
  • 1
  • 1
Aivean
  • 10,692
  • 25
  • 39