On Sun, Dec 29, 2013 at 4:24 PM, larry google groups <
lawrencecloj...@gmail.com> wrote:

> And here is where the greatest disappointment arrives: neither 
> future<http://clojuredocs.org/clojure_core/clojure.core/future>
>  nor promise <http://clojuredocs.org/clojure_core/clojure.core/promise> in
> Clojure supports listening for completion/failure asynchronously. ... 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<http://clojuredocs.org/clojure_core/clojure.core/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<http://nurkiewicz.blogspot.no/2013/02/javautilconcurrentfuture-basics.html>
>     and Guava to some 
> extent<http://nurkiewicz.blogspot.no/2013/02/advanced-listenablefuture-capabilities.html>
>
> 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.
>
>
>
> So, I am curious if we will see support for completion listeners. Or is
> there a feeling that stuff like core.async has addressed some of this and
> nothing more is needed?
>

Well, core.async directly subsumes the entire functionality of promises.
And adds watch capability:

(let [c (chan)] ; instead of (promise)
  (go ; prefer thread if the long calculation is REALLY long
    (>! c (long calculation here)) ; instead of (future (deliver ...))
    (close! c))
  (let [d (go
             (let [x (<! c)]
               (println "Special delivery!" x) ; Watch!
               x))]
    (<!! d))) ; (instead of @c)

The channel here is used just like a promise: the first go asynchronously
does some calculation and delivers a result to it, then closes it, which
results in behavior like a delivered promise, i.e. it can't be delivered to
again. One could just use (<!! c) to grab the result (blocking until
available), like @some-promise. The second go block shows one bit of added
power: you can add an asynchronous watch for the result to appear. The go
block takes it from c as soon as it's available, executes the watch (in
this case a simple println), and then evaluates to the delivered value.
Letting [d (go ...)] results in d being bound to a channel that will get
this value pushed onto it (and then get closed) when the go block
terminates, and (<!! d) blocks the main thread until this happens and then
evaluates to the result.

The one thing "missing" is that whereas you can @some-promise repeatedly to
retrieve the value once it's delivered, you can't (<!! d) repeatedly
without getting the delivered value once and then nils thereafter. But you
could change it slightly to combine channels *with* promise:

(let [c (chan)]
  (go
    (>! c (long calculation here))
    (close! c))
  (let [d (promise)]
    (go
      (let [x (<! c)]
        (println "Special delivery!" x)
        (deliver d x)))]
    @d))

Now d is a promise, but it's delivered by a go block that can also
asynchronously watch for completion. But leaving d as a channel might be
better. You can always put the result in another kind of container once
it's delivered, and deref that repeatedly, and by leaving d a channel you
can use alt! and timeout on this channel, for example, to add timeout
behavior to your checking for delivery.

Futures used locally can be "watched asynchronously" trivially: instead of
(future (long calculation here)) you have (future (let [x (long calculation
here)] (hey-x-got-finished! x) x)). The (hey-x-got-finished! x) call will
take place in the future's thread. If you don't want it blocking the main
thread (i.e. you want (hey-x-got-finished! x) able to run concurrently with
whatever derefs the future, instead of the latter maybe being blocked
longer) then you'd use (future (let [x (long calculation here)] (future
(hey-x-got-finished! x)) x)). :)

That deals with watching for completion. Watching for error is as easy with
future: wrap a try ... catch around the body inside (future ...). With
promise/core.async, you need the try block in the producer's code, whereas
core.async lets you add completion watching at the consumer end as easily
as at the producer. (Even to a normal promise generated by, say, code you
can't change: just (let [c (thread @p)] ...) to turn promise p into channel
c that will get @p pushed onto it when p is delivered; thread is used
instead of go because a blocking job in a go is not recommended. Wrap the
@p in something that will do something with its argument and return its
argument to add a completion watch, for values of "do something" that can
of course include spawning further async processes.)

Futures you get passed from code you don't control (never heard of this
happening, but I suppose it *could* happen) can be treated analogously to
promises where you control only the consumer: for error checking you're
SOL, but for completion watching you just wrap the deref in core.async's
(thread ...). You can then spawn alarm processes from in there upon
delivery, use the channel returned by (thread ...) in alts with timeout,
use the channel in a go, just do a blocking take from it, and etc.

-- 
-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to