Welcome to the Future in Scala

Reading Time: 4 minutes

You have units of work that you want to run asynchronously, so you don’t block while they’re running. A future gives you a simple way to run an algorithm concurrently. A future starts running concurrently when you create it and returns a result at some point, well, in the future. In Scala, we call that a future returns eventually.

The Future instance is a handle to an eventually available result. You can continue doing other work until the future completes, either successfully or unsuccessfully.

Why sequential code can be wrong?

Suppose you want to prepare a cappuccino. You could simply execute the following steps, one after another.

  • Grind the required coffee beans.
  • Boil some water.
  • Brew an espresso using the ground coffee and the boiled water.
  • Froth some milk.
  • Combine the espresso and the frothed milk to make a cappuccino
import scala.util.Try

//Some type aliases just for getting more meaningfull method signatures

type CoffeeBeans = String

type GrindedCoffee = String

case class Water(temperature: Int)

type Milk: String

type FrothedMilk: String

type Espresso: String

type Cappuccino: String

//implementation of sequential steps

Implementing instructions has several advantages: You get very readable step-by-step instructions on what to do. Moreover, you will not have any confusion when you are preparing the cappuccino this way, since you follow step-by-step information.

def grindCoffeeBeans(beans: CoffeeBeans): GrindedCoffee = s"grinded final

coffee of $beans"


def boilWater(water: Water): Water = water.copy(temperature =

85)


def frothMilk(milk: Milk): FrothedMilk = s"frothed $milk"


def brew(coffee: GrindedCoffee, boiledWater: Water): Espresso
 
= "espresso"


def combine(espresso: Espresso, frothedMilk: FrothedMilk):
 
Cappuccino = "cappuccino coffee"


 // going through these steps sequentially


 def prepareCappuccino(): Try[Cappuccino] = for {

  ground   <- Try(grindCoffeeBeans("Eurobean Beans"))

  water    <- Try(boilWater(Water(25)))

  espresso <- Try(brew(ground, water))

  foam     <- Try(frothMilk("milk"))

}yield combine(espresso, foam)

Preparing a Cappuccino like this step-by-step means that your brain and body are on a wait during a large part of the whole process. While waiting for the ground coffee you are effectively blocked. Only when that’s finished, you be able to start boiling some water and so on. This is clearly a waste of valuable resources.

Execute Concurrently

It is very likely that you would want to initiate multiple steps and have them execute concurrently. Once you see that the water and the ground coffee are ready, you’d start brewing the espresso, in the meantime already starting the process of frothing the milk.

It’s really no different when writing a piece of software. A web server only has so many threads for processing requests and creating appropriate responses. You don’t want to block these valuable threads by waiting for the results of a database query or a call to another HTTP service. Instead, you want an asynchronous programming model and non-blocking IO, so that, another request will also process in an asynchronous manner without waiting for the other requests to complete.

Are concurrency and parallelism the same thing in Scala?

Concurrency and parallelism are not the same things. Parallelism implies concurrency, however, concurrency does not imply parallelism.

Concurrency means several tasks or threads of execution are performed at the same time, whether they do related work or not. Parallelism means that we divide one task into multiple threads of execution that run concurrently.

Working with Future

Let’s rewrite our Cappuccino example to make use of the Future type. Firstly we need to rewrite all of the functions that will execute concurrently so that, they immediately return a Future instead of computing their result in a blocking way:

import scala.concurrent.Future

import scala.concurrent.ExecutionContext.Implicits.global

import scala.concurrent.duration._

import scala.util.Random


def grindCoffeeBeans(beans: CoffeeBeans):

Future[GrindedCoffee] = Future{

  println("Start grinding..........")

  Thread.sleep(Random.nextInt(2000))

  println("finished grinding.....")

  s"grinded coffee of $beans"

}


def boilWater(water: Water): Future[Water] = Future{

  println("Heating the water now..........")

  Thread.sleep(Random.nextInt(2000))

  println("hot its hot.....")

  water.copy(temperature = 85)
}


def frothMilk(milk: Milk): Future[FrothedMilk] = Future{

  println("milk frothing system engaged..........")

  Thread.sleep(Random.nextInt(2000))

  println("shutting down milk frothing system.....")

   s"frothed $milk"
}


def brew(coffee: GrindedCoffee, boiledWater: Water): 

Future[Espresso] = Future{

  println("happy brewing...........")

  Thread.sleep(Random.nextInt(2000))

  println("Its brewed")

   "espresso"
}

Semantics of Future

Scala’s Future[T], residing in the scala.concurrent package is a container type, representing a computation that is supposed to eventually result in a value of type T. Alas, the computation might go wrong or timeout, so when the future is completed, it may not have been successful after all, in which case it contains an exception instead.

Future is a write-once container- after a future has been completed, it is effectively immutable.

Callback Methods

The following statements describe the use of the callback methods that can be used with futures.

  • When a future completes, the callback method is execute asynchronously.
  • The callback methods are onComplete, onSuccess, and onFailure.
  • A callback method is execute by some thread, sometime after the future is complete. But “There is no guarantee that it will be call by the thread that completes the future or the thread that creates the callback.”
  • There is no guarantee in the order in which callbacks are call.
  • onComplete takes a callback function of type Try[T] => U .
  • onSuccess and onFailure take partial functions. You only need to handle the desired case.

Conclusion

A Future is basically a container for a value that may or may not happen at some point in time, coupled with its execution context. By execution context, I mean the code that actually executes your future.

Written by 

Gulshan Singh is a Software Consultant at Knoldus Inc. Software Developers are forever students. He is passionate about Programming.