Saytiras
Saytiras

Reputation: 149

Understanding the interplay between futures and lazy-seq

One week ago I asked a similar question (Link) where I learned that the lazy nature of map makes the following code run sequential.

(defn future-range
  [coll-size num-futures f]
  (let [step (/ coll-size num-futures)
        parts (partition step (range coll-size))
        futures (map #(future (f %)) parts)]      ;Yeah I tried doall around here...
    (mapcat deref futures)))

That made sense. But how do I fix it? I tried doall around pretty much everything (:D), a different approach with promises and many other things. It just doesn't want to work. Why? It seems to me that the futures don't start until mapcat derefs them (I made some tests with Thread/sleep). But when I fully realize the sequence with doall shouldn't the futures start immediately in another thread?

Upvotes: 0

Views: 257

Answers (1)

jkj
jkj

Reputation: 2611

It seems you are already there. It works if you wrap (map #(future (f %)) parts) in (doall ...). Just restart your repl and start from clean slate to ensure you are calling the right version of your function.

(defn future-range
  [coll-size num-futures f]
  (let [step (/ coll-size num-futures)
        parts (partition step (range coll-size))
        futures (doall (map #(future (f %)) parts))]
    (mapcat deref futures)))

You can use the following to test it out.

(defn test-fn [x]
  (let [start-time (System/currentTimeMillis)]
    (Thread/sleep 300)
    [{:result x
      :start start-time
      :end-time (System/currentTimeMillis)}]))

(future-range 10 5 test-fn)

You could also just use time to measure that doing 5 times (Thread/sleep 300) only takes 300 ms of time:

(time (future-range 10 5 (fn [_] (Thread/sleep 300))))

Upvotes: 2

Related Questions