Mike Slinn

Better Syntactic Sugar for Scala Futures

Published 2017-05-25.
Time to read: 2 minutes.

This page is part of the posts collection, categorized under Scala.
Sugar cubes

Ever since Scala’s Futures were initially provided as part of Akka 2.0, programmers have been confused by the non-intuitive syntax. That is why ScalaCourses.com dedicates an entire lecture to For-comprehensions With Futures, as part of an 8 lecture series on Scala Futures within the Intermediate Scala course.

As Viktor Klang points out in his blog, the following code does not run 3 Futures in parallel:

Shell
def doSomething(someParameter: SomeType)
               (implicit ec: ExecutionContext): Future[Something] =
  for {
    v1 <- Future(someCalculation())
    v2 <- Future(someOtherCalculation())
    v3 <- Future(someDifferentCalculation())
  } yield doSomethingWith(v1, v2, v3)

The compiler has no way of ascertaining the programmer’s intent – perhaps it is desirable for some reason to run the 3 Futures one after the other.

Viktor suggests this syntax to make futures run in parallel:

Shell
def doSomething(someParameter: SomeType)
               (implicit ec: ExecutionContext): Future[Something] =
  for {
    f1 = Future(someCalculation())
    f2 = Future(someOtherCalculation())
    f3 = Future(someDifferentCalculation())
    v1 <- f1
    v2 <- f2
    v3 <- f3
  } yield doSomethingWith(v1, v2, v3)

While Viktor’s solution works, it is verbose. Worse, it silently fails to run the futures in parallel if the programmer accidentally writes even one of the expressions out of order:

Shell
def doSomething(someParameter: SomeType)
               (implicit ec: ExecutionContext): Future[Something] =
  for {
    f1 = Future(someCalculation())
    v1 <- f1
    f2 = Future(someOtherCalculation())
    v2 <- f2
    f3 = Future(someDifferentCalculation())
    v3 <- f3
  } yield doSomethingWith(v1, v2, v3)

Again, the compiler has no way of ascertaining the programmer's intent, so it should not generate an error or warning message.

We Need A Macro

A new right-associative operator, implemented as a Scala macro, would make the programmer's intent clear. Let’s call this operator <=: (parallel generator). The above code could be rewritten using the parallel generation operator like this:

Shell
def doSomething(someParameter: SomeType)
               (implicit ec: ExecutionContext): Future[Something] =
  for {
    v1 <=: Future(someCalculation())
    v2 <=: Future(someOtherCalculation())
    v3 <=: Future(someDifferentCalculation())
  } yield doSomethingWith(v1, v2, v3)

There is no longer any doubt that the programmer intended for all 3 futures to run in parallel.

The macro would examine all the for-expression’s generators and expand consecutive expressions that use the <=: operator to a series of variable declarations using the = operator followed by a series of assignments using the <- operator, exactly as we saw earlier:

Shell
def doSomething(someParameter: SomeType)
              (implicit ec: ExecutionContext): Future[Something] =
  for {
    f1 = Future(someCalculation())
    f2 = Future(someOtherCalculation())
    f3 = Future(someDifferentCalculation())
    v1 <- f1
    v2 <- f2
    v3 <- f3
  } yield doSomethingWith(v1, v2, v3)

Because the compiler ‘knows’ that the programmer’s intention was to run the Futures in parallel, this sort of error could cause an error or warning message to be generated:

Shell
def doSomething(someParameter: SomeType)
              (implicit ec: ExecutionContext): Future[Something] =
  for {
    v1 <=: Future(someCalculation())
    x <- List(1, 2, 3)
    v2 <=: Future(someOtherCalculation())
    y <- List("a", "b")
    v3 <=: Future(someDifferentCalculation())
  } yield doSomethingWith(v1, v2, v3)

... or the macro might reorder the generators and issue a warning that it did so.

Include the Macro in Scala 2.12.x

This macro should become part of the Scala language so long as Futures are part of the standard runtime. If and when Futures are hived out of the standard runtime, the macro should be packaged with Futures.


PS: @flaviusbraz tweeted on May 25, 2017: “Easily doable with a macro transformation. In fact, I’ve implemented this transformation at Twitter, but it’s not open source.”



* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.