Using 'future' ?

8 views
Skip to first unread message

Gorsal

unread,
Oct 18, 2009, 5:59:12 PM10/18/09
to Clojure
I'm attempting to use the function future to start something in
another thread. However, in the netbeans enclojure plugin, when i type
it into the repl, it becomes non-responsive. Is this just a bug in the
enclojure plugin or would this be normal?

(future
(let [input-stream (:input-stream *lisp*)]
(loop [the-char (.read input-stream)]
(if (not (= the-char -1)) (jprint (char the-char)))
(recur (.read input-stream)))))

John Harrop

unread,
Oct 18, 2009, 7:29:39 PM10/18/09
to clo...@googlegroups.com
We'd need to know more about the vars specific to your project named here, notably *lisp* and jprint. The latter is called as a function or macro and the former holds an input stream. If it redirects standard input, in particular, that will redirect it away from the REPL.

Most likely, though, it's an unfortunate effect of how futures print themselves:

user=> (future (* 3 4))
#<Object$Future$IDeref@1dacb2b: 12>

As you can see, futures get their values to print themselves. So executing any (future ...) expression at the REPL causes the REPL to block until the future spits out a result. If that input stream is not generating an EOF for a while, the REPL isn't responding again for a while.

It's hard to use a future unless you keep a reference to it, so I suggest evaluating (def foo (future ...)) at the REPL:

user=> (future (do (Thread/sleep 1000) (println "boo!") (* 3 4)))
boo!
#<Object$Future$IDeref@62ad0d: 12>

There should be a second's delay before the boo! and the rest of the output appears.

(Enclojure users will find the "boo!" in the *out* pane rather than the Repl pane after executing this.)

user=> (def calc-twelve (future (Thread/sleep 1000) (do (println "boo!") (* 3 4))))
#'user/calc-twelve
user=>
boo!

This time, the #'user/calc-twelve and the next user=> prompt should appear instantly, and after a second's delay, the boo! should appear. (Again, enclojure users will find the boo! in a different pane. They can make the delay bigger, say changing 1000 to 10000, see the next prompt appear, switch to the *out* pane, and watch the boo! appear after a few more seconds.)

Timothy Pratley

unread,
Oct 18, 2009, 9:31:42 PM10/18/09
to Clojure


> Most likely, though, it's an unfortunate effect of how futures print
> themselves:
>
> user=> (future (* 3 4))
> #<Object$Future$IDeref@1dacb2b: 12>

I agree - this is the cause, if you really wanted to do this at the
REPL maybe you can get around it like this:
user=> (do (future (* 3 4)) nil)
nil

Gorsal

unread,
Oct 18, 2009, 10:15:46 PM10/18/09
to Clojure
Thanks, this works.

Gorsal

unread,
Oct 18, 2009, 10:22:25 PM10/18/09
to Clojure
Hey, is there any way to separate the out view from the repl view so i
don't have to switch back and forth?

Gorsal

unread,
Oct 19, 2009, 12:39:43 AM10/19/09
to Clojure
So now that the future is working, I'm attempting to print from an
actual java thread. Like this

(defmacro with-thread [nm & body]
`(let [thread# (Thread. #(fn [] (do ~@body)))]
~@(if nm `((.setName thread# ~nm)))
(.start thread#)
thread#))

(with-thread nil (println "HasdfasdfasdfasdfasdfasdfasdfasdfI"))

Except no output! Eeek!!! What am i doing wrong?

John Harrop

unread,
Oct 19, 2009, 1:15:58 AM10/19/09
to clo...@googlegroups.com
On Sun, Oct 18, 2009 at 10:22 PM, Gorsal <se...@tewebs.com> wrote:
Hey, is there any way to separate the out view from the repl view so i
don't have to switch back and forth?

In Enclojure? Unfortunately, apparently not. It's possible to undock the REPL and make it a free-floating window, but all three of the tabs Repl, *err*, and *out* come with it. There doesn't seem to be a way to separate these from one another.

Timothy Pratley

unread,
Oct 19, 2009, 1:18:31 AM10/19/09
to Clojure

On Oct 19, 3:39 pm, Gorsal <s...@tewebs.com> wrote:
> Except no output! Eeek!!! What am i doing wrong?

Looks like #(fn ... is meant to be #(do ~@body)
#(fn declares a function which creates a function, so your thread is
returning a function instead of executing it.

user=> (macroexpand-1 '(with-thread nil (println
"HasdfasdfasdfasdfasdfasdfasdfasdfI")))
(clojure.core/let [thread__2__auto__ (java.lang.Thread. (fn* []
(clojure.core/fn [] (do (println
"HasdfasdfasdfasdfasdfasdfasdfasdfI")))))] (.start thread__2__auto__)
thread__2__auto__)

Adrian Cuthbertson

unread,
Oct 19, 2009, 1:18:49 AM10/19/09
to clo...@googlegroups.com
The following seems to do it;

(defmacro with-thread [nm & body]
`(let [thread# (Thread. (fn [] (do ~@body)))]
(if ~nm (.setName thread# ~nm))
(.start thread#)
thread#))

(with-thread "foo" (println "HasdfasdfasdfasdfasdfasdfasdfasdfI"))
#<Thread Thread[foo,5,main]>
user=> HasdfasdfasdfasdfasdfasdfasdfasdfI

Also, macroexpand-1 really helps when writing macros. You can see
exactly how it's being expanded and it's then somewhat easier to
debug;

(macroexpand-1 '(with-thread nil (println "HasdfasdfasdfasdfasdfasdfasdfasdfI"))
(clojure.core/let [thread__112__auto__ (java.lang.Thread.
(clojure.core/fn [] (do (println
"HasdfasdfasdfasdfasdfasdfasdfasdfI"))))] (if nil (.setName
thread__112__auto__ nil)) (.start thread__112__auto__)
thread__112__auto__)

Regards, Adrian.

Adrian Cuthbertson

unread,
Oct 19, 2009, 1:28:05 AM10/19/09
to clo...@googlegroups.com
Just an addendum to my last post - without the .start you can test it better;

(defmacro with-thread [nm & body]
`(let [thread# (Thread. (fn [] (do ~@body)))]
(if ~nm (.setName thread# ~nm))
;(.start thread#)
thread#))

(def th (with-thread "foo" (println "HasdfasdfasdfasdfasdfasdfasdfasdfI")))

(.start th)
nil
HasdfasdfasdfasdfasdfasdfasdfasdfI

(.getName th)
"foo"

- Adrian.

Gorsal

unread,
Oct 19, 2009, 1:44:29 AM10/19/09
to Clojure
Thanks. I think now that i recall i have made that mistake before. I
must differentiate - fn and #()!

John Harrop

unread,
Oct 19, 2009, 1:45:12 AM10/19/09
to clo...@googlegroups.com
Get rid of the extra #:

(defmacro with-thread [nm & body]
 `(let [thread# (Thread. (fn [] (do ~@body)))]

    ~@(if nm `((.setName thread# ~nm)))
    (.start thread#)
    thread#))

#(fn [] foo) is equivalent to (fn [] (fn [] foo)), i.e. is a zero-argument function that returns a zero argument function. So your thread's Runnable had no side effects and the return value (which, if invoked, WOULD have had side effects) was discarded and never invoked.

Meikel Brandmeyer

unread,
Oct 19, 2009, 1:24:37 AM10/19/09
to clo...@googlegroups.com
Hi,

Just a suspicion. (I don't know, what Repl you use.)

If the Repl sets up special *in* and *out* bindings, these will be
different for the other thread. Eg. VimClojure does that. you try the
following:

(let [out *out*]
(with-thread nil
(binding [*out* out]
(println "muhahahahahaha"))))

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages