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) } }
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Summary
There are two points I was thinking about last night when I thought about all of this:
- I personally don’t like the name
Future
, and preferConcurrentTask
. It’s easier to reason about. - 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.