Can "for" be enhanced to not have to take a binding?

0 views
Skip to first unread message

CuppoJava

unread,
May 7, 2009, 5:11:04 PM5/7/09
to Clojure
I'm trying to accomplish the following:
Create a lazy sequence of calls to f() while pred() is true.
And an elegant way to do this seems to be:

(for [:while (pred)] (f))

which doesn't work because (for) requires a binding.
This can be worked around with:

(for [i (constantly 0) :while (pred)] (f))

which is not as elegant.

Does anyone else think this is a worthwhile to "for"? Thanks for your
opinions.
-Patrick

Kevin Downey

unread,
May 7, 2009, 5:14:22 PM5/7/09
to clo...@googlegroups.com
user=> (doc take-while)
-------------------------
clojure.core/take-while
([pred coll])
Returns a lazy sequence of successive items from coll while
(pred item) returns true. pred must be free of side-effects.
nil
user=>
--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

Meikel Brandmeyer

unread,
May 7, 2009, 5:17:20 PM5/7/09
to clo...@googlegroups.com
Hi,

together with repeatedly:

(take-while pred (repeatedly f)))

Sincerely
Meikel

> --~--~---------~--~----~------------~-------~--~----~
> 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
> 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
> -~----------~----~----~----~------~----~------~--~---
>

Christophe Grand

unread,
May 7, 2009, 5:24:55 PM5/7/09
to clo...@googlegroups.com
CuppoJava a écrit :

> I'm trying to accomplish the following:
> Create a lazy sequence of calls to f() while pred() is true.
> And an elegant way to do this seems to be:
>
> (for [:while (pred)] (f))
>
> which doesn't work because (for) requires a binding.
> This can be worked around with:
>
> (for [i (constantly 0) :while (pred)] (f))
>

(pred) does not depend on (f)? Is there some IO or mutable state?

--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.blogspot.com/ (en)


CuppoJava

unread,
May 7, 2009, 5:40:30 PM5/7/09
to Clojure
Yeah (pred) is not supposed to depend on any items inside f.

This is why (take-while pred (repeatedly f)))
won't work in this situation.

(take-while) will always take an element out of f, so that it can be
tested using (pred). I don't want any elements of (f) to be looked at
if (pred) is false.

----USE CASE----
I'm using it in combination with some Java libraries. The following
seems like a very clojure-ish way of doing things.

(for [:while (Mouse/hasEvent)] (Mouse/getEvent))

so this returns a nice lazy stream of mouse events, which can be
processed however i like.

Meikel Brandmeyer

unread,
May 7, 2009, 5:44:11 PM5/7/09
to clo...@googlegroups.com
Hi,

lazy-seq to the rescue:

(defn mouse-seq
[]
(lazy-seq
(when (Mouse/hasEvent)
(cons (Mouse/getEvent) (mouse-seq)))))

Sincerely
Meikel

CuppoJava

unread,
May 7, 2009, 5:54:16 PM5/7/09
to Clojure
Thanks Meikel.
That certainly works. But don't you find:

(for [:while (Mouse/hasEvent)] (Mouse/getEvent))

much shorter and easier to understand?

Meikel Brandmeyer

unread,
May 7, 2009, 6:13:51 PM5/7/09
to clo...@googlegroups.com
Hi,

Am 07.05.2009 um 23:54 schrieb CuppoJava:

> But don't you find:
>
> (for [:while (Mouse/hasEvent)] (Mouse/getEvent))
>
> much shorter and easier to understand?

Actually: no. I think of for as a way to transform
a sequence, not constructing a completely new
one. There are constructs like iterate and repeatedly
for that. If they are not sufficient, one can drop to
lazy-seq. (And sometimes dropping to lazy-seq
can yield clearer code than some contorted
map filter concat map combination)

In fact I think the lazy-seq version is pretty self-explaining.

On the other hand I should use for more often.
So maybe this view is all wrong. YMMV in the end.

Sincerely
Meikel

Jarkko Oranen

unread,
May 7, 2009, 6:15:19 PM5/7/09
to Clojure
I don't, really. for is a list comprehension, and so it needs
bindings... Something to generate the list from.

Also, how is a lazy sequence of mouse events supposed to work? does it
block? What happens when there is no mouse event? does the sequence
just end? What if you happen to take the last event, and then the
sequence ends, after which another event comes in? for wouldn't know
what to do in the general case.

Anyway, I don't think it's worth the trouble; it's not especially
functional style either. If you need such a construct, I believe it
would be best to make a new function, with a more fitting name.

--
Jarkko

CuppoJava

unread,
May 7, 2009, 6:35:56 PM5/7/09
to Clojure
Thanks for your replies.

I've always thought of "for" as a generator. Basically just a loop
that produces a lazy collection. So it actually seems very natural to
me.

But anyway, I think I shall just write my own generator function using
lazy-seq and be done with it then.

Again, thanks for offering your opinions. They were insightful.
-Patrick

Mark Reid

unread,
May 7, 2009, 8:04:09 PM5/7/09
to Clojure
Hi,

This `lazy-seq` over a `when` and `cons` idiom seems fairly common. Is
there any reason there is not a function for it? For example:

(defn cons-while
"Lazily creates a sequence by repeatedly calling f until pred is
false"
[pred f]
(lazy-seq
(when pred
(cons f (cons-while pred f)))))

I haven't tested it but I suspect the following should now work:

(cons-while (Mouse/hasEvent) (Mouse/getEvent))

Regards,

Mark
--
http://mark.reid.name

On May 8, 7:44 am, Meikel Brandmeyer <m...@kotka.de> wrote:
> Hi,
>
> lazy-seq to the rescue:
>
> (defn mouse-seq
>    []
>    (lazy-seq
>      (when (Mouse/hasEvent)
>        (cons (Mouse/getEvent) (mouse-seq)))))
>
> Sincerely
> Meikel
>
> Am 07.05.2009 um 23:40 schrieb CuppoJava:
>
>
>
>
>
> > Yeah (pred) is not supposed to depend on any items inside f.
>
> > This is why (take-while pred (repeatedly f)))
> > won't work in this situation.
>
> > (take-while) will always take an element out of f, so that it can be
> > tested using (pred). I don't want any elements of (f) to be looked at
> > if (pred) is false.
>
> > ----USE CASE----
> > I'm using it in combination with some Java libraries. The following
> > seems like a very clojure-ish way of doing things.
>
> > (for [:while (Mouse/hasEvent)] (Mouse/getEvent))
>
> > so this returns a nice lazy stream of mouse events, which can be
> > processed however i like.
> > >
>
>
>  smime.p7s
> 5KViewDownload

Laurent PETIT

unread,
May 8, 2009, 2:43:45 AM5/8/09
to clo...@googlegroups.com
Hi,

2009/5/8 Mark Reid <mark...@gmail.com>


Hi,

This `lazy-seq` over a `when` and `cons` idiom seems fairly common. Is
there any reason there is not a function for it? For example:

(defn cons-while
 "Lazily creates a sequence by repeatedly calling f until pred is
false"

s/false/logical false/

 [pred f]
 (lazy-seq
   (when pred
     (cons f (cons-while pred f)))))

I haven't tested it but I suspect the following should now work:

(cons-while (Mouse/hasEvent) (Mouse/getEvent))

(cons-while #(Mouse/hasEvent) #(Mouse/getEvent))

or maybe create a general version of repeatedly-while :

(defn repeatedly-while [pred f] (take-while pred (repeatedly f)))

and call it like this also:

(repeatedly-while #(Mouse/hasEvent) #(Mouse/getEvent))

Then I remark it is just a minor variation on Meikel's first answer :

(take-while #(no-op-pred) (repeatedly f)))


(not tested)

Daniel Lyons

unread,
May 8, 2009, 12:15:06 PM5/8/09
to clo...@googlegroups.com

On May 7, 2009, at 4:15 PM, Jarkko Oranen wrote:
> Also, how is a lazy sequence of mouse events supposed to work? does it
> block? What happens when there is no mouse event? does the sequence
> just end? What if you happen to take the last event, and then the
> sequence ends, after which another event comes in? for wouldn't know
> what to do in the general case.


I think a facility like this is part of the underpinning of functional
reactive programming (FRP), which is an active research area in
designing interactive systems such as GUIs functionally rather than
object orientedly (if that's a word).

Consider a trivial demo where you want to continuously update the
mouse's coordinates in a text box on a window. You could just map a
function which updates that text box across the potentially infinite
list of mouse events. Yes, it would block until the next event came
in, but the UI is in a consistent state while the mouse isn't moving.
One has to assume that sequences of events are potentially infinite.

Haskell is pretty good at the infinite list part, but it's pretty bad
at the side-effect part and timing part. I tried pretty hard to read
Conal Eliot's paper "Simply Efficient FRP" <http://conal.net/papers/simply-reactive/
> but I never got far enough in Haskell to actually understand what
it's saying. It looks like something hard to do in Haskell.

It's also been done in Scheme, specifically the FrTime "language" in
DrScheme. There's a paper about it here: <http://citeseer.ist.psu.edu/cache/papers/cs/32750/ftp:zSzzSzftp.cs.brown.eduzSzpubzSztechreportszSz03zSzcs03-20.pdf/cooper04frtime.pdf
> which I haven't even tried to read.

There's also an interesting Javascript implementation called Flapjax,
which was pretty under-documented when I heard about it, but makes it
a little easier to get the gist of the idea: <http://www.flapjax-lang.org/
>

From what little I have gathered about the concept, Clojure ought to
be in a unique position to leverage it, since Clojure has ready access
to both GUI frameworks and lazy sequences from within an easy-to-
reason-about strict language. Much of the work in Haskell FRP must be
to cope with state and time, whereas in other languages it must be to
introduce the right kinds of laziness and parallelism. Clojure has a
good mixture of these ingredients already.

There could be extremely novel ways of approaching well-known problems
hiding within Clojure right now.


Daniel Lyons
http://www.storytotell.org -- Tell It!

Mark Reid

unread,
May 9, 2009, 7:06:00 AM5/9/09
to Clojure
Hi Laurent,

Thanks for the feedback. I'm still a bit stuck though since neither my
proposal nor yours work for the type of application I had in mind.

Here's a complete program which highlights the problems:

; ---- Begin lazyread.clj ----
(import '(java.io FileReader BufferedReader PrintWriter))

(def filename "test.data")

; Write out a small test file. Numbers 0 to 99, one per line.
(with-open [data (PrintWriter. filename)]
(dotimes [i 100] (.println data i)))

; An attempt at capturing the general idiom that doesn't work
(defn cons-while
[pred f]
(lazy-seq
(when pred
(cons f (cons-while pred f)))))

; Another attempt that doesn't work
(defn repeatedly-while
[pred f]
(take-while pred (repeatedly f)))

; A specific implementation for the problem at hand
(defn lazy-read [reader]
(lazy-seq
(when (.ready reader)
(cons (.readLine reader) (lazy-read reader)))))

(print "lazy-read: ")
(with-open [data (BufferedReader. (FileReader. filename))]
(prn (take 10 (lazy-read data))))

(print "cons-while: ")
(with-open [data (BufferedReader. (FileReader. filename))]
(prn (take 10 (cons-while (.ready data) (.readLine data)))))

(print "repeatedly-while: ")
(with-open [data (BufferedReader. (FileReader. filename))]
(prn (take 10 (repeatedly-while #(.ready data) #(.readLine data)))))

; ---- End lazyread.clj ----

Running this will write out a small test file named `test.data` in the
current directory with the numbers 0-99 one per line and then use
three techniques to lazily read in the first 10 lines.

The first technique, `lazy-read`, is an explicit use of the `lazy-seq`
+ `when` + `cons` combination I wish to generalise. The second one,
`cons-while`, is the attempt I made and the third, `repeatedly-while`
is your suggestion.

Here's the output I get when I run this as a script from the command
line:
----
$ clj lazyread.clj
lazy-read: ("0" "1" "2" "3" "4" "5" "6" "7" "8" "9")
cons-while: ("0" "0" "0" "0" "0" "0" "0" "0" "0" "0")
Exception in thread "main" java.lang.RuntimeException:
java.lang.RuntimeException: java.lang.IllegalArgumentException: Wrong
number of args passed to: user$eval--38$fn (scratch.clj:0)
----

Unsurprisingly, the problem-specific method seems to work fine. The
`cons-while` method returns the wrong output and the last technique
fails because of issues surrounding the macro expansion of (.ready
data) and the #(...) macro that have been discussed elsewhere.

Is it possible to get the `repeatedly-while` version working with
calls to Java without making the calling pattern too convoluted?

Also, can anyone explain why the `cons-while` approach only appears to
be repeatedly reading the first line?

Finally, would a macro approach be better for this type of problem?

Thanks,

Mark.
--
http://mark.reid.name

Meikel Brandmeyer

unread,
May 9, 2009, 1:05:37 PM5/9/09
to clo...@googlegroups.com
Hi,

Am 09.05.2009 um 13:06 schrieb Mark Reid:

> ; ---- Begin lazyread.clj ----
> (import '(java.io FileReader BufferedReader PrintWriter))
>
> (def filename "test.data")
>
> ; Write out a small test file. Numbers 0 to 99, one per line.
> (with-open [data (PrintWriter. filename)]
> (dotimes [i 100] (.println data i)))
>
> ; An attempt at capturing the general idiom that doesn't work
> (defn cons-while
> [pred f]
> (lazy-seq
> (when pred
> (cons f (cons-while pred f)))))

> ; ---- End lazyread.clj ----

There is a mistake in this function: pred should be (pred)
as well as f should be (f).
Furthermore I would call it repeatedly-while.

(defn repeatedly-while
[pref f]
(lazy-seq
(when (pred)
(cons (f) (cons-while pred f)))))

Then this should work:

(repeatedly-while #(.ready reader) #(.readLine reader))

I didn't test it, though.

Sincerely
Meikel

Laurent PETIT

unread,
May 9, 2009, 3:15:31 PM5/9/09
to clo...@googlegroups.com
Hi, the version of repeatedly-while I submitted still takes an argument for the predicate function, that would be the value of the last generated item in the list:


(defn repeatedly-while
 [pred f]
 (take-while pred (repeatedly f)))

So if you want to call it with a no-arg "predicate", you must adapt it :

(repeatedly-while (fn [ _ ] (no-arg-pred)) f)
instead of
(repeatedly-while no-arg-pred f)

(notice the underscore that emphazises the fact that we don't care about the argument).

Or create a less general function that accepts only no-arg predicates:
(defn repeatedly-while2 [no-arg-pred f]
  (take-while (fn [ _ ] (no-arg-pred) f))

(not tested)

But this is a less general function ...

Regards,

--
Laurent

2009/5/9 Mark Reid <mark...@gmail.com>

Mark Reid

unread,
May 10, 2009, 1:00:19 AM5/10/09
to Clojure
Hi Meikel,

On May 10, 3:05 am, Meikel Brandmeyer <m...@kotka.de> wrote:
> There is a mistake in this function: pred should be (pred)
> as well as f should be (f).

> Furthermore I would call it repeatedly-while.
>
> (defn repeatedly-while
>    [pref f]
>    (lazy-seq
>      (when (pred)
>        (cons (f) (cons-while pred f)))))
>
> Then this should work:
>
> (repeatedly-while #(.ready reader) #(.readLine reader))

Yes, that works now. Thanks.

So the extra parentheses are there to force the evaluation of the
predicate and function?

Mark Reid

unread,
May 10, 2009, 1:03:07 AM5/10/09
to Clojure
Hi Laurent,

On May 10, 5:15 am, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> So if you want to call it with a no-arg "predicate", you must adapt it :
>
> (repeatedly-while (fn [ _ ] (no-arg-pred)) f)
> instead of
> (repeatedly-while no-arg-pred f)

I think that this is too convoluted a calling pattern. For my
purposes, my preference is for the amended `repeatedly-while` function
Miekel suggested.

Thanks,

Mark.

Laurent PETIT

unread,
May 10, 2009, 1:19:34 AM5/10/09
to clo...@googlegroups.com


2009/5/10 Mark Reid <mark...@gmail.com>


No problem, but you can still consider (for the definition of the function) the equivalent higher-order version I provided later in the my post (you didn't answer to this one):

(defn repeatedly-while [no-arg-pred f]

  (take-while (fn [ _ ] (no-arg-pred) f))

whose usage is the same as Meikel's one,


Regards,

--
Laurent


 


Thanks,

Mark.



Meikel Brandmeyer

unread,
May 10, 2009, 2:54:09 AM5/10/09
to clo...@googlegroups.com
Hi Laurent,

Am 10.05.2009 um 07:19 schrieb Laurent PETIT:

> No problem, but you can still consider (for the definition of the
> function) the equivalent higher-order version I provided later in
> the my post (you didn't answer to this one):
>
> (defn repeatedly-while [no-arg-pred f]
> (take-while (fn [ _ ] (no-arg-pred) f))
>
> whose usage is the same as Meikel's one,

This is not equivalent to my version. The calling site
looks the same, but it still consumes one element of
the seq before evaluating the predicate. That is the
way take-while works. But it doesn't help in the given
reader example, since you read before checking,
whether the reader is actually ready...

Sincerely
Meikel

Meikel Brandmeyer

unread,
May 10, 2009, 2:57:54 AM5/10/09
to clo...@googlegroups.com
Hi Mark,

Am 10.05.2009 um 07:00 schrieb Mark Reid:

> So the extra parentheses are there to force the evaluation of the
> predicate and function?

Well. The don't "force" the evaluation. They just call the provided
function. "pred" evaluates to a function. In our example #(.ready
reader).
"(pred)" simply call this function. As "(repeatedly-while pred f)"
calls the
function named by "repeatedly-while" passing the functions named by
pred and f as arguments. I wouldn't call this "forcing"...

Sincerely
Meikel

Laurent PETIT

unread,
May 10, 2009, 3:12:20 AM5/10/09
to clo...@googlegroups.com


2009/5/10 Meikel Brandmeyer <m...@kotka.de>

Wooops, you're totally right,
 

Sincerely
Meikel



Reply all
Reply to author
Forward
0 new messages