On the Scala Future, and semantics

In programming, semantics can be important. While in some cases I don’t care too much how developers name classes and variables, in some cases monikers can cause problems. (Perhaps even in that sentence.)

In the case of Scala, I found that the name Future slowed me down for a while; for my brain, it made it harder for me to grasp the concept. The more I thought about it, and the more I read about it, I realized that if I thought of it as a ConcurrentTask instead of a Future, I could understand the concept more easily.

Fortunately in Scala you can rename things when you import them, so in the following example I rename Future to ConcurrentTask so you can see what I’m talking about:

package futures

import scala.concurrent.{Future => ConcurrentTask}           // rename
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import Utils.sleep

object FutureAsConcurrentTask extends App {

  // run some long-running task (task has type Future[Int] in this example)
  val task = ConcurrentTask {
    Cloud.executeLongRunningTask
  }

  // whenever the task completes, execute this code
  task.onComplete {
    case Success(value) => println(s"Got the callback, value = $value")
    case Failure(e) => println(s"D'oh! The task failed: ${e.getMessage}")
  }

  // do your other work
  println("A ..."); sleep(100)
  println("B ..."); sleep(100)
  println("C ..."); sleep(100)
  println("D ..."); sleep(100)
  println("E ..."); sleep(100)
  println("F ..."); sleep(100)

}

Personally, when I look at this code and see a name like ConcurrentTask and a variable named task, I can easily understand the code. I can imagine that whenever the task completes, the onComplete callback will be run. In this case, because you don’t know when executeLongRunningTask task will complete, the output from the program will be indeterminate, but it will look something like this:

A ...
B ...
C ...
D ...
E ...
Got the callback, value = 42
F ...

The Future syntax

I think you’ll agree that the previous code is simple and easy to read. Now, by just changing the name ConcurrentTask back to Future, and changing the object name from task to future, you have this code:

val future = Future {
  Cloud.executeLongRunningTask
}

// whenever the future completes, come here
future.onComplete {
  case Success(value) => println(s"Got the callback, value = $value")
  case Failure(e) => println(s"D'oh! The Future failed: ${e.getMessage}")
}

I know this is “just” semantics, but to me, that code is harder to comprehend. I have to think about Future and future much harder than ConcurrentTask and task, and that extra grokking effort slowed me down, and made it harder for me to grasp the concepts.

What’s in a name?

I thought about this for a while last night, and came up with a collection of possible replacement names for a Future, including these:

Worker
Delegate
AsyncJob
AsyncWorker

and more radical names like these:

Spacetime
Bot
Task

I tried to brainstorm this last night, and came up with 41 different names, but in the end I thought the best name was ConcurrentTask, which I showed above. Where did I get that name? Interestingly, it came from the Scala documentation:

Completion

For the sake of completion, other objects used in the first example shown above are shown next. Here’s a file named Cloud.scala:

package futures

import scala.util.Random
import Utils.sleep

object Cloud {

  def executeLongRunningTask: Int = {
    sleep(Random.nextInt(500))
    42
  }

}

And here’s a file named Utils.scala:

package futures

object Utils {

  def sleep(duration: Long) { Thread.sleep(duration) }
  
}

Summary

There are two points I was thinking about last night when I thought about all of this:

  1. I personally don’t like the name Future, and prefer ConcurrentTask. It’s easier to reason about.
  2. A great thing about Scala is that you can change the name during the import process.

I hope I demonstrated those points in this example.