Skip to main content

Promises and futures in Clojure

Clojure, being designed for concurrency is a natural fit for our Back to the Future series. Moreover futures are supported out-of-the-box in Clojure. Last but not least, Clojure is the first language/library that draws a clear distinction between futures and promises. They are so similar that most platforms either support only futures or combine them. Clojure is very explicit here, which is good. Let's start from promises:


Promise is a thread-safe object that encapsulates immutable value. This value might not be available yet and can be delivered exactly once, from any thread, later. If other thread tries to dereference a promise before it's delivered, it'll block calling thread. If promise is already resolved (delivered), no blocking occurs at all. Promise can only be delivered once and can never change its value once set:

(def answer (promise))


(deliver answer 42)
answer is a promise var. Trying to dereference it using @answer or (deref answer) at this point will simply block. This or some other thread must first deliver some value to this promise (using deliver function). All threads blocked on deref will wake up and subsequent attempts to dereference this promise will return 42 immediately. Promise is thread safe and you cannot modify it later. Trying to deliver another value to answer is ignored.


Futures behave pretty much the same way in Clojure from user perspective - they are containers for a single value (of course it can be a map or list - but it should be immutable) and trying to dereference future before it is resolved blocks. Also just like promises, futures can only be resolved once and dereferencing resolved future has immediate effect. The difference between the two is semantic, not technical. Future represents background computation, typically in a thread pool while promise is just a simple container that can be delivered (filled) by anyone at any point in time. Typically there is no associated background processing or computation. It's more like an event we are waiting for (e.g. JMS message reply we wait for).

That being said, let's start some asynchronous processing. Similar to Akka, underlying thread pool is implicit and we simply pass piece of code that we want to run in background. For example to calculate the sum of positive integers below ten million we can say:

    [sum (future (apply + (range 1e7)))] 
    (println "Started...") 
    (println "Done: " @sum)
sum is the future instance. "Started..." message appears immediately as the computation started in background thread. But @sum is blocking and we actually have to wait a little bit1 to see the "Done: " message and computation results. And here is where the greatest disappointment arrives: neither future nor promise in Clojure supports listening for completion/failure asynchronously. The API is pretty much equivalent to very limited java.util.concurrent.Future<T>. We can create future, cancel it, check whether it is realized? (resolved) and block waiting for a value. Just like Future<T> in Java, as a matter of fact the result of future function even implements java.util.concurrent.Future<T>. As much as I love Clojure concurrency primitives like STM and agents, futures feel a bit underdeveloped. Lack of event-driven, asynchronous callbacks that are invoked whenever futures completes (notice that add-watch doesn't work futures - and is still in alpha) greatly reduces the usefulness of a future object. We can no longer:

  • map futures to transform result value asynchronously
  • chain futures
  • translate list of futures to future of list
  • ...and much more, see how Akka does it and Guava to some extent
That's a shame and since it's not a technical difficulty but only a missing API, I hope to see support for completion listeners soon. For completeness here is a slightly bigger program using futures to concurrently fetch contents of several websites, foundation for our web crawling sample:

(let [
    top-sites `("" "" "" "")
    futures-list (doall (
            map #(
                future (slurp (str "http://" %))
    contents (map deref futures-list)
(doseq [s contents] (println s))
Code above starts downloading contents of several websites concurrently. map deref waits for all results one after another and once all futures from futures-list all completed, doseq prints the contents (contents is a list of strings).

One trap I felt into was the absence of doall (that forces lazy sequence evaluation) in my initial attempt. map produces lazy sequence out of top-sites list, which means future function is called only when given item of futures-list is first accessed. That's good. But each item is accessed for the first time only during (map deref futures-list). This means that while waiting for first future to dereference, second future didn't even started yet! It starts when first future completes and we try to dereference the second one. That means that last future starts when all previous futures are already completed. To cut long story short, without doall that forces all futures to start immediately, our code runs sequentially, one future after another. The beauty of side effects.

1 - BTW (1L to 9999999L).sum in Scala is faster by almost an order of magnitude, just sayin'... - see comments by Rave Star below.


  1. " it'll block infinitely" -- I think you meant "indefinitely" as in an arbitrary amount of time until the value is available. "Infinitely" makes it sound like it's blocked forever.

  2. "neither future nor promise in Clojure does not support " -- double negative. I think you mean "neither ... supports".

    1. Thank you so much for these corrections. Actually by "infinitely" I meant "will wait forever if not delivered", but admittedly it was confusing. Double negation fixed as well. Thanks again!

  3. scala> def bench() = {
    val s=time();
    val col = (1L to 9999999L).toArray;
    val y = col.sum;
    val e = time();
    println("ms: " + (e - s).toString())
    bench: ()Unit

    scala> bench
    ms: 900


    (time (apply + (range 1e7)))
    "Elapsed time: 543.722219 msecs"

    Where is this "order of magnitude slower" for Clojure?

    1. I'll try to explain how Clojure code works, and why it's equivalent to Scala's one (in previus comment).

      Clojure's (range) function creates lazy collection which elements are created when they are accessed for the first time. You can easily deduce this by a fact that a second call to previously created (called) elements takes less time:

      Lets define a range:
      (def v (range 1e7))
      this code returns immediately, because a range between 0 and 1e7 is only a lazy collection. (take n (range coll)) also creates lazy collection (lazy collection from lazy collection).

      Collection elements type:
      (type (first v))
      -> java.lang.Long

      (def v (range 1e7))
      lazy1 (take 1000000 v)
      lazy2 (take 2000000 v)
      (time (vec lazy1)) ;1
      (time (vec lazy1)) ;2
      (time (vec lazy2)) ;3
      (time (vec lazy2)) ;4
      "Elapsed time: 466.080775 msecs"
      "Elapsed time: 58.530581 msecs"
      "Elapsed time: 620.464425 msecs"
      "Elapsed time: 90.684377 msecs"

      Notice how second call to collection items (which were already created in previous step) takes significantly less time (7x ~ 8x less). If I want more elements these must be created first, so (vec lazy2) takes more time. (vec) creates a vector from a collection. Vector is not a lazy collection.

      This shows that a call to (apply + ...) creates a whole list of 1e7 elements which is equivalent to (1L to 9999999L).toArray.sum in Scala.

      When you call (1L to 9999999L).sum in Scala nothing is created. Return value is deduced from left and right limit and a step, which in this case is 1.
      This task reduces to simple calculation: sum = n(n+1)/2, where n is 9999999.

    2. 1. First of all let me thank you for your insights, comments and explanation. I was doing my benchmarks on Scala 2.9.1 that does not have this aggressive Range.sum optimization. Of course comparing brute-force Clojure with O(1) equation in Scala wouldn't be fair. Here are some more details:

      2. On my pretty old computer (Intel Core 2 Duo CPU T7300 @ 2.00GHz x 2) with Scala 2.9.1 and Clojure 1.4.0 benchmark results are as follows:

      a) Clojure: (time (apply + (range 1e7))) -> 2400-2500 milliseconds

      b) Scala: (1L to 9999999L).sum -> ~310 milliseconds

      Thus on my machine Scala is about 8 times faster (which is close to an order of magnitude)

      3. Scala 2.10 has optimized Range.sum method that takes advantage of simple O(1) equation rather than brute force approach. 2.9.1 uses standard generic implementation based on foldLeft() which results in 310 ms runtime on average.

      4. Converting Range.toArray is a bit biassed because creating 80 MB array at once might introduce significant overhead

      5. Range.toStream is much closer to lazy seq in Clojure. But (1L to 9999999L).toStream.sum is agonizing slow. I guess Clojure has a much better implementation of lazy sequences, as they are ubiquitous. Clojure definitely wins here

      6. My verdict was based on benchmarks in point (2). However as you can see results are heavily dependant on the machine, data structure used and optimizations. Thus I am erasing my comment from the article. I don't want to be controversial or fire up another holy war. But again, I truly appreciate your comments and time.

  4. Useful samples and explanations, both in article and in the comments. Keep up the great work!


Post a Comment