Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
;;; Future pipelining macro.
;;; Behaves like a let, but wraps everything in futures - running your bindings concurrently
;;; Computation takes only as long as the longest-running bind
(ns user
(:use clojure.walk clojure.test))
(defn cols
"Projection of n columns from coll"
[n coll]
(apply map vector (partition n coll)))
(defmacro let-futures
"Bindings wrapped in futures. Derefs in bindings and body made implicit."
[bindings & body]
(let [[locals procs] (cols 2 bindings)
lmap (zipmap locals (map #(list `deref %) locals))]
`(let ~(vec (interleave locals (map #(cons `future (list %))
(postwalk-replace lmap procs))))
~@(postwalk-replace lmap body))))
(deftest test-cols
(is (= [[:a :b :c] [:A :B :C]] (cols 2 [:a :A :b :B :c :C])))
(is (= [[:a :b :c] [:A :B :C] [10 20 30]] (cols 3 [:a :A 10 :b :B 20 :c :C 30]))))
(deftest test-let-futures
(letfn [(sleepy-fn [ms] (Thread/sleep ms) 1)]
;; running time should be ~500ms
(is (= 3 (time
(let-futures [f1 (sleepy-fn 500)
f2 (+ (sleepy-fn 500) f1)
f3 (+ (sleepy-fn 500) f2)]
f3))))
(is (= 10 (let-futures [x 10]
x)))))

oranenj commented Apr 27, 2010

You shouldn't cons the unqualified deref and future symbols, as they might be shadowed in the expansion context.

#(listderef %)and#(cons future (list %)) avoid the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment