Clojure Futures Docs and Functionality

47 views
Skip to first unread message

Ryan Senior

unread,
Jun 21, 2010, 1:34:11 AM6/21/10
to clo...@googlegroups.com
I figured before I added a ticket, I'd post here.  I was using the functions for Futures in Clojure and found for all of the future functions (like future, future-done?, future-cancel etc) it was nicely abstracted and there was no need to know exactly what kind of object was passed into or returned from the functions.  When it become a problem, is when I wanted to get the value returned by the future with a timeout.  To do this, I needed to know that the future function returned an implementation of java.util.concurrent.Future, and that interface had a get method that accepts a timeout value.  I was then able to make a Java call to get the future value with a timeout.

Improvements/Questions:

1 - I think a good improvement would be updating the docs for the future related functions to indicate what kind of objects are passed in and returned.  Users would then not have to go through the source to see what's being returned.  This could also be useful to know if users were getting java.util.concurrent.Future instances from other places and wanted to use them with the Clojure API.

2 - Is there a reason that get with a timeout from java.util.concurrent.Future is not included in the Clojure future functions?  It seems to be the only one missing and having that function would make the details around what kind of object is returned/passed in by these functions less important.  Maybe future-await or something?  What sort of time parameter would this new function would accept?  The java.util.concurrent.Future.get method uses an int and the java.util.concurrent.TimeUnit enum.

Should I log a ticket on these items?

Thanks,

Ryan

Daniel Werner

unread,
Jun 23, 2010, 2:15:56 PM6/23/10
to Clojure
Hello Ryan,

On Jun 21, 7:34 am, Ryan Senior <senior.r...@gmail.com> wrote:
> 1 - I think a good improvement would be updating the docs for the future
> related functions to indicate what kind of objects are passed in and
> returned.  Users would then not have to go through the source to see what's
> being returned.  This could also be useful to know if users were getting
> java.util.concurrent.Future instances from other places and wanted to use
> them with the Clojure API.

Judging from previous discussions on this list, I get the feeling that
relying too much on the Java implementation details underlying the
public Clojure APIs is discouraged. This certainly makes sense
considering that apparently one of the future plans (no pun intended)
is to give the CLR and JavaScript ports of the language more focus.

> 2 - Is there a reason that get with a timeout from
> java.util.concurrent.Future is not included in the Clojure future
> functions?  It seems to be the only one missing and having that function
> would make the details around what kind of object is returned/passed in by
> these functions less important.  Maybe future-await or something?  What sort
> of time parameter would this new function would accept?  The
> java.util.concurrent.Future.get method uses an int and the
> java.util.concurrent.TimeUnit enum.

IMHO this seems like a useful improvement. Maybe your proposal would
draw more attention if you backed it up with a patch -- even a
preliminary one? :-)

Have a nice day,
--
Daniel

Ryan Senior

unread,
Jun 24, 2010, 11:27:01 PM6/24/10
to clo...@googlegroups.com
Hi Daniel,

On Wed, Jun 23, 2010 at 1:15 PM, Daniel Werner <daniel....@googlemail.com> wrote:

Judging from previous discussions on this list, I get the feeling that
relying too much on the Java implementation details underlying the
public Clojure APIs is discouraged. This certainly makes sense
considering that apparently one of the future plans (no pun intended)
is to give the CLR and JavaScript ports of the language more focus.

I don't know much about the CLR version of Clojure but can understand that.  My goal was just trying eliminate the need look at the source code to know about the timeout feature of the Futures.  Having the below function solves that problem.


IMHO this seems like a useful improvement. Maybe your proposal would
draw more attention if you backed it up with a patch -- even a
preliminary one? :-)


What I was thinking for a future-await function was:

(defn- convert-time-unit [unit]
    (case unit
          :nanoseconds (java.util.concurrent.TimeUnit/NANOSECONDS)
          :microseconds (java.util.concurrent.TimeUnit/MICROSECONDS)
          :milliseconds (java.util.concurrent.TimeUnit/MILLISECONDS)
          :seconds (java.util.concurrent.TimeUnit/SECONDS)
          :minutes (java.util.concurrent.TimeUnit/MINUTES)
          :hours (java.util.concurrent.TimeUnit/HOURS)
          :days (java.util.concurrent.TimeUnit/DAYS)
          (throw (IllegalArgumentException. (str unit " is not a valid unit of time")))))

(defn future-await
    "Returns the value of the future just like a deref.  If the future has not completed by timeout, nil is returned"
      ([^java.util.concurrent.Future f timeout-in-millis]
         (future-await f timeout-in-millis :milliseconds))
      ([^java.util.concurrent.Future f timeout unit]
           (try
             (->> unit
                     convert-time-unit
                     (.get f timeout))
            (catch java.util.concurrent.TimeoutException e
              nil))))

It's usage is below

(def fut
       (future
        (Thread/sleep 1000000)
        "done"))
(future-await fut 1000) ;throws exception
(future-await fut 2 :seconds) ;throws exception

(def fut2
       (future
        (Thread/sleep 10000)
        "done"))
(future-await fut2 2 :minutes) ; => "done"


 -Ryan

Daniel Werner

unread,
Jun 25, 2010, 2:48:06 PM6/25/10
to clo...@googlegroups.com
On 25 June 2010 05:27, Ryan Senior <senio...@gmail.com> wrote:
> (future-await fut2 2 :minutes) ; => "done"

Your implementation points into the right direction (again, IMHO). I'd
like to offer two suggestions:

1. Leave out the three-arg version of future-await. The time unit
conversion seems somewhat superfluous, with little gain for the lines
of code added. Rich argued against writing thin wrappers around Java
stuff as well. And personally, I'd probably only ever use the two-arg
version with an integer in milliseconds, since that's what most
methods/functions are using anyway. (If you know of use cases for and
trust the system clock to act on milliseconds correctly, you could
also make the integer represent nanoseconds instead.)

2. Returning nil to represent timeout is dangerous here -- the future
may have completed correctly and returned nil! It should be perfectly
okay and idiomatic here to let the TimeoutException escape, as long as
you document this behaviour.

What do others think?
--
Daniel

Meikel Brandmeyer

unread,
Jun 26, 2010, 1:56:48 AM6/26/10
to clo...@googlegroups.com
Hi,

Am 25.06.2010 um 20:48 schrieb Daniel Werner:

> On 25 June 2010 05:27, Ryan Senior <senio...@gmail.com> wrote:
>> (future-await fut2 2 :minutes) ; => "done"
>

> What do others think?

I agree – in particular for point 2.

For point 1: I think "2 :minutes" is not very clojure-like. It sound more like Ruby: 2.minutes. A more clojure-like approach would be to an use optional keyword argument.

(defn future-await
[fut & {:keys [timeout]}]
....)

(future-await zukunft)
(future-await zukunft :timeout time-out-in-ms)

Or of course the simple two-arg-version.

Sincerely
Meikel

Ryan Senior

unread,
Jun 26, 2010, 10:36:42 AM6/26/10
to clo...@googlegroups.com
Sounds good to me.  The first version of that function I wrote without returning nil, but just bubbling up the exception.  I changed it because I couldn't think of another area of the Clojure core that threw an exception like that.  Looking through some of the agents code, I think there are some similar scenarios that do throw an exception.

As far as the seconds/minutes etc stuff.  I just added that to not lose anything from the Java API.  Getting rid of that, the much simpler function looks like:


(defn future-await
  "Returns the value of the future just like a deref.  If the future has not completed by timeout, nil is returned"
  [^java.util.concurrent.Future f timeout-in-millis]
    (.get f timeout-in-millis java.util.concurrent.TimeUnit/MILLISECONDS))

Usage is same as before:

(def fut

       (future
        (Thread/sleep 10000)
        "done"))
(future-await fut 1) ; throws java.util.concurrent.TimeoutException

(def fut

       (future
        (Thread/sleep 10000)
        "done"))
(future-await fut 100000) ; "done"

-Ryan



--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply all
Reply to author
Forward
0 new messages