This is the fifth of several posts describing protips for scala.concurrent.Future
.
For the previous post, click here.
Transforming when and where
Most readers will be familiar with Future
-based code which looks something like this:
import scala.concurrent._
import ExecutionContext.Implicits._
def someCalculation(i: Int): Future[Int] = Future.successful(i) // placeholder
val f = someCalculation(42).map(_ * 10).filter(_ > 0).map(_ / 2))
f foreach println // prints 210
Let’s deconstruct what’s going on here:
First we perform someCalculation
and then we multiply the result by 10, then we filter the result for positive integers and then we divide by 2.
But, for each of these steps we create an additional Future
and schedule it to run on our designated ExecutionContext
—in this case introducing significant overhead for these very simple operations.
A cheaper way (sometimes)
If we know that we’re going to use the same ExecutionContext, and we’re willing to make more calculations in one go, we can since Scala 2.12 use transform
and run the transformations on Try
directly,
as transform
takes a function from Try[T]
to Try[U]
, which looks something like this:
import scala.concurrent._
import ExecutionContext.Implicits._
def someCalculation(i: Int): Future[Int] = Future.successful(i) // placeholder
val f = someCalculation(42).transform(_.map(_ * 10).filter(_ > 0).map(_ / 2))
f foreach println // prints 210
andThen more fun
Knowing this, we can compose many of the simple transformations we want to apply as functions from Try
-> Try
, as shown here:
import scala.concurrent._
import scala.util.Try
import ExecutionContext.Implicits._
def someCalculation(i: Int): Future[Int] = Future.successful(i) // placeholder
val times10 = (t: Try[Int]) => t.map(_ * 10)
val filterPos = (t: Try[Int]) => t.filter(_ > 0)
val divBy2 = (t: Try[Int]) => t.map(_ / 2)
val f = someCalculation(42).transform(times10 andThen filterPos andThen divBy2)
f foreach println // prints 210
Click here for the next part in this blog series.
Cheers,
√