delay/force definitely do not do what I'm describing. What we want
to is something like a continuation as mentioned by Chouser. We want
to block the thread of execution until some point in the future when
the values become available.
I'm not sure that what you described describes what you want :)
Yes, if you want to block the current thread to wait for a value
computed by a background thread, you need promises and futures.
However, you were explicitly talking about a use "outside the context
of concurrency". If you don't want concurrency, only delayed
execution, then use delay instead.
In your example you have to *know* that all the tests have run in
order to accumulate results.
No you don't — force is synchronous. By the time that `doall` has
finished, all the tests have run and their results have been
accumulated inside the delays.
As I said, if you want to run tests in parallel, then you're not
talking "outside the context of concurrency". Futures are awesome, but
you can't argue that they're not a parallel construct.
By using promise/deliver you can remove yourself from caring about
that at all.
With a recursive data structure this becomes annoying very quickly,
you have to bookkeep where you are if you use delay/force.
No you don't — simply always call `force`, just as you always call
`deref`, and the delayed computation will be done if necessary. (In
fact, you can use "@", just as you can with refs and promises.)
Here's an example of a trivial recursive arithmetic evaluator that
prints the whole tree, returning a delay which captures the execution.
When forced, it prints the arithmetic results back up the tree.
Execution occurs once.
(defn form->delay [v]
(if (number? v)
(do
(println " Saw number" v)
(delay v))
(let [[op & args] v]
(println "Seen form with operator" op ", args" args)
(let [delays (map form->delay args)]
(delay
(let [res (eval `(~op ~@(map force delays)))]
(println "Result is" res)
res))))))
user=> (let [de (form->delay `(+ 5 (- 3 2)))]
(println "First run: " @de)
(println "No bookkeeping: " @de)
(println "Delay: " de)
@de)
Seen form with operator clojure.core/+ , args (5 (clojure.core/- 3 2))
Saw number 5
Seen form with operator clojure.core/- , args (3 2)
Saw number 3
Saw number 2
Result is 1
Result is 6
First run: 6
No bookkeeping: 6
Delay: #<de...@58e22f2b: 6>
6
You'll see that:
* It runs on one thread
* There is no bookkeeping of what's been forced and what has not; you
can force multiple times without re-execution
* It can do arbitrary work during the tree walk (printing "Seen
form..."), and during evaluation (printing "Result is"), and the two
phases are separate.
By creating the future computations to be performed during the
traversal you later only need to deliver each individual test
result. As tests come in they automatically trigger the next level
of computation (the aggregate result). You don't need track
relationships between tests at all because that was determined by
the first traversal.
There are only two places where futures might apply in what you're
talking about:
* To run tests in parallel, which you explicitly disregarded
* To have the tests depend on each other's values (which doesn't seem
very smart to me).
Use futures and promises for parallelism or dataflow-style work. Use
delay for non-parallel, synchronous delayed execution.
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en