Re: non-lazy clojure?

2014-06-05 Thread Lee Spector

A followup on these issues, after experimenting a bit with reducers, just for 
anyone who may be interested:

- Naive use of reducers in place of seq functions, throughout my program, got 
messy and led to some new problems with memory management, I think (or at least 
to jerkier execution over time). I realized that reducers probably shouldn't be 
used so indiscriminately.

- With more moderate use of reducers, and using fold, I do indeed see 
performance improvements relative to sequential code and also relative to the 
previous version of my code that used agents for concurrency. I don't actually 
seem to be getting much benefit from the way that reducers avoid building 
intermediate results -- all of the benefit appears to be due to using a 
combination of r/fold and one r/map to process collection elements 
concurrently. I've only done small numbers of tests and they all involve 
randomness, but I do see improvements on the order of 1/3.

- The best behaving/looking approach for me seems to be to stick with lazy 
sequence operations and aggressive de-lazifying for the bulk of my code 
(possibly cleaning some things up by defining my own forv, repeatv, etc., or 
possibly rewriting some of the core lazy sequence functions to not be lazy in 
the first place), but to use r/fold and r/map for concurrency for my top-level 
tasks. I'm getting the best results by using reducers only in one place, in my 
definition of a concurrent mapping function, which I then use only for 
top-level functions (never nested):

(defn pmapall
   [f coll]
   (if single-thread-mode
 (doall (map f coll))
 (r/fold 1 r/cat r/append! (r/map f coll

This replaces my previous agent-based version:

(defn pmapall
   [f coll]
   (if single-thread-mode
 (doall (map f coll))
 (let [agents (map #(agent % :error-handler 
   (fn [agnt except] (clojure.repl/pst except 1000) 
(System/exit 0))) 
   coll)]
   (dorun (map #(send % f) agents))
   (apply await agents)
   (doall (map deref agents)

The one that uses reducers is neater and seems to make my program run faster, 
although I'm not really sure why.

In any event, I'm using reducers now only as an alternative concurrency 
mechanism and solving my original problems by de-lazification.

 -Lee



On Jun 4, 2014, at 7:21 PM, Lee Spector lspec...@hampshire.edu wrote:

 
 On Jun 4, 2014, at 1:29 PM, Timothy Baldridge tbaldri...@gmail.com wrote:
 
 Although your original complaint was about clojure seqs being lazy. It 
 should be noted that reducers are also lazy down to the point of a fold or 
 reduce, so I'm not sure what you're really getting there. It wouldn't be 
 hard at all to write map, filter, remove, etc. in terms of list operations. 
 Perhaps that's what you're looking for? If I understand your original 
 complaint, you want map, filter, etc, to be eager.
 
 
 True, my original concern was (and my main concern still is) to avoid crashes 
 (in this case, stack overflow errors) that stem from unexpected (to me) 
 consequences of laziness.
 
 Those problems could indeed be solved by rewriting map and filter etc, or 
 (easier) by wringing out the laziness wherever it arises, e.g. by forcing 
 everything to be a vector (or a list, as you suggest). It's a little 
 cumbersome to do this everywhere, though, and I was wondering if there was an 
 existing library or approach for this issue.
 
 Then, however, it was suggested that I could have my cake and another kind of 
 cake too, by using reducers. The suggestion was that this could banish the 
 laziness-related issues while also providing significant performance 
 improvements. Sounds good to me! I'm still working on getting my current 
 project to behave well using reducers, but it seems promising.
 
 
 On the other hand, time spent learning how lazy seqs work, and the caveats 
 involved will make it easier for you to understand the code produced by the 
 rest of the community. So perhaps that's the better option. 
 
 I agree that it's important to understand lazy sequences, and I do think I 
 understand the core concepts reasonably well (and I've implemented lazy 
 evaluation in other languages, etc.). But I've also traced some really 
 annoying and hard to find bugs to unexpected (and sometimes never fully 
 explained) consequences of laziness, so I'd like to find the best ways to 
 avoid it when I don't really want it. If that best way turns out to make 
 things run faster too then that'd be fantastic.
 
 -Lee

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

Re: non-lazy clojure?

2014-06-05 Thread Lee Spector

Oops -- something was wrong with my benchmarks, and my improvements on the 
order of 1/3 was wrong. I still see improvements with r/fold as compared to my 
agent-based approach, but the difference now appears to be only something like 
1/20.

 -Lee

On Jun 5, 2014, at 7:19 PM, Lee Spector lspec...@hampshire.edu wrote:

 
 A followup on these issues, after experimenting a bit with reducers, just for 
 anyone who may be interested:
 
 - Naive use of reducers in place of seq functions, throughout my program, got 
 messy and led to some new problems with memory management, I think (or at 
 least to jerkier execution over time). I realized that reducers probably 
 shouldn't be used so indiscriminately.
 
 - With more moderate use of reducers, and using fold, I do indeed see 
 performance improvements relative to sequential code and also relative to the 
 previous version of my code that used agents for concurrency. I don't 
 actually seem to be getting much benefit from the way that reducers avoid 
 building intermediate results -- all of the benefit appears to be due to 
 using a combination of r/fold and one r/map to process collection elements 
 concurrently. I've only done small numbers of tests and they all involve 
 randomness, but I do see improvements on the order of 1/3.
 
 - The best behaving/looking approach for me seems to be to stick with lazy 
 sequence operations and aggressive de-lazifying for the bulk of my code 
 (possibly cleaning some things up by defining my own forv, repeatv, etc., or 
 possibly rewriting some of the core lazy sequence functions to not be lazy in 
 the first place), but to use r/fold and r/map for concurrency for my 
 top-level tasks. I'm getting the best results by using reducers only in one 
 place, in my definition of a concurrent mapping function, which I then use 
 only for top-level functions (never nested):
 
 (defn pmapall
   [f coll]
   (if single-thread-mode
 (doall (map f coll))
 (r/fold 1 r/cat r/append! (r/map f coll
 
 This replaces my previous agent-based version:
 
 (defn pmapall
   [f coll]
   (if single-thread-mode
 (doall (map f coll))
 (let [agents (map #(agent % :error-handler 
   (fn [agnt except] (clojure.repl/pst except 
 1000) (System/exit 0))) 
   coll)]
   (dorun (map #(send % f) agents))
   (apply await agents)
   (doall (map deref agents)
 
 The one that uses reducers is neater and seems to make my program run faster, 
 although I'm not really sure why.
 
 In any event, I'm using reducers now only as an alternative concurrency 
 mechanism and solving my original problems by de-lazification.
 
 -Lee
 
 
 
 On Jun 4, 2014, at 7:21 PM, Lee Spector lspec...@hampshire.edu wrote:
 
 
 On Jun 4, 2014, at 1:29 PM, Timothy Baldridge tbaldri...@gmail.com wrote:
 
 Although your original complaint was about clojure seqs being lazy. It 
 should be noted that reducers are also lazy down to the point of a fold or 
 reduce, so I'm not sure what you're really getting there. It wouldn't be 
 hard at all to write map, filter, remove, etc. in terms of list operations. 
 Perhaps that's what you're looking for? If I understand your original 
 complaint, you want map, filter, etc, to be eager.
 
 
 True, my original concern was (and my main concern still is) to avoid 
 crashes (in this case, stack overflow errors) that stem from unexpected (to 
 me) consequences of laziness.
 
 Those problems could indeed be solved by rewriting map and filter etc, or 
 (easier) by wringing out the laziness wherever it arises, e.g. by forcing 
 everything to be a vector (or a list, as you suggest). It's a little 
 cumbersome to do this everywhere, though, and I was wondering if there was 
 an existing library or approach for this issue.
 
 Then, however, it was suggested that I could have my cake and another kind 
 of cake too, by using reducers. The suggestion was that this could banish 
 the laziness-related issues while also providing significant performance 
 improvements. Sounds good to me! I'm still working on getting my current 
 project to behave well using reducers, but it seems promising.
 
 
 On the other hand, time spent learning how lazy seqs work, and the caveats 
 involved will make it easier for you to understand the code produced by the 
 rest of the community. So perhaps that's the better option. 
 
 I agree that it's important to understand lazy sequences, and I do think I 
 understand the core concepts reasonably well (and I've implemented lazy 
 evaluation in other languages, etc.). But I've also traced some really 
 annoying and hard to find bugs to unexpected (and sometimes never fully 
 explained) consequences of laziness, so I'd like to find the best ways to 
 avoid it when I don't really want it. If that best way turns out to make 
 things run faster too then that'd be fantastic.
 
 -Lee

--
Lee Spector, Professor of Computer Science
Cognitive Science, Hampshire College
893 

Re: non-lazy clojure?

2014-06-05 Thread Gary Johnson
Fair enough. Fortunately, Clojure provides so many different tools to 
select from in creating you perfect recipe. ;-)

I'm glad to hear that reducers ultimately provided you with some benefits 
over your previous concurrency approach.
The one thing that seems rather odd to me though is that your group-size is 
1. I'm presuming that the function you're r/mapping must take a substantial 
amount of time and resources for that to be efficient. Have you 
experimented with larger group sizes to avoid too much thread swapping?

  ~Gary

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-05 Thread Lee Spector

On Jun 5, 2014, at 8:51 PM, Gary Johnson gwjoh...@uvm.edu wrote:

 Fair enough. Fortunately, Clojure provides so many different tools to select 
 from in creating you perfect recipe. ;-)
 
 I'm glad to hear that reducers ultimately provided you with some benefits 
 over your previous concurrency approach.
 The one thing that seems rather odd to me though is that your group-size is 
 1. I'm presuming that the function you're r/mapping must take a substantial 
 amount of time and resources for that to be efficient. Have you experimented 
 with larger group sizes to avoid too much thread swapping?

Yes, I've tried other group sizes and I get nearly no speedup over 
single-threaded code with the default group size (512). Group size 1 seems to 
be about as good as any other value i've tried.

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Lee Spector
 On Jun 2, 2014, at 7:14 PM, Gary Johnson gwjoh...@uvm.edu wrote:
 
 Hey Lee,
 
  I would second Jozef's suggestion that you look into using the reducers 
 library when you need non-lazy sequence operations. [etc]

On Jun 2, 2014, at 10:38 PM, Lee Spector lspec...@hampshire.edu wrote:

 
 Gary: That's compelling indeed, and I will look into it more!


Some quick notes and a question from my first look into this:

- I watched a Rich Hickey reducers video, was riveted, and see that they are 
beautiful. I particularly appreciated his brief aside about how lazy seqs have 
independent utility.

- In practice, for the little program I posted about previously, switching to 
reducers involved a couple of initially unexpected complications, some of now 
make sense to me but others of which don't fully: results of reducers sometimes 
have to be explicitly converted, e.g. with into []; r/map doesn't take 
multiple collections; I don't immediately see elegant reducer-based approaches 
to uses of for or repeat, etc.

- My initial swapping of clojure.core.reducers functions for lazy seq (and 
agent-based parallel computing) functions seems to make my performance worse 
rather than better. I realize that there are several possible explanations for 
this and I have to look at my usage more carefully. It's definitely possible 
that I'm doing more than one thing wrong, but one question that this leads to 
is:

- If I operate on a vector with a sequence of r/map and r/filter operations and 
finally with into [] to get back a vector, then I think that fold will be 
called within the call to into, and that parallelism should happen there... 
right? But if that's right, how do I control the group size that the call to 
fold uses in this case? I see how to specify the group size for a direct call 
to fold, but not for other function in the library.

Thanks,

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Gary Johnson
Hey Lee,

  (vec ...) is NOT the same as (into [] ...) in this case.

  Whenever you use a reducing function, like r/map, r/filter, r/mapcat, and 
so on, you are not, in fact, performing any computations on the collection 
to which you apply it. These functions simply wrap the collection with a 
kind of delayed operation that will later be triggered by applying reduce 
to it.

  So, if I have a collection called samples, and I run (r/map inc samples), 
I just get a reducible object back. Same thing with a chain of these 
functions as in (r/filter odd? (r/map inc samples)). I just get back a 
reducible object (which itself is wrapping another reducible object). When 
you run reduce on this whole thing, each of the delayed operations are 
combined together into one giant reduce function, as if you had written 
something like this:

  (reduce + (r/filter odd? (r/map inc samples)))
   = (reduce (fn [sum sample] (let [sample' (inc sample)] (if (odd? 
sample') (+ sample' sum) sum))) 0 samples)

  This is the reason that you need to use (into [] ...) rather than (vec 
...). Running vec on a reducible object will just throw a RuntimeException. 
When you use into, you will be applying reduce (since that's how it is 
implemented), and as long as you use into with a vector, map, or set, it 
will be run using transients for efficiency.

  Uses transients:

  (into [] (r/map inc (range 10)))
  (into {} (r/map #(vector % 0) (range 10)))
  (into #{}| (r/map inc (range 10)))

  Doesn't use transients (but still works). Note that the list will have 
been reversed because of the nature of list consing:
  (into () (r/map inc (range 10)))

Alright, hopefully that's enough from me for now. Good luck with your 
program.

  ~Gary

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Lee Spector

On Jun 4, 2014, at 12:59 PM, Gary Johnson gwjoh...@uvm.edu wrote:

 Hey Lee,
 
   (vec ...) is NOT the same as (into [] ...) in this case.
[etc]

Thanks Gary -- very clear and helpful.

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Gary Johnson
Hey Lee, answers below. Also make sure to read my other post at 12:59pm 
today regarding the behavior of vec vs. into for reducible collections.

On Wednesday, June 4, 2014 12:51:45 PM UTC-4, Lee wrote:

 Some quick notes and a question from my first look into this: 

 - I watched a Rich Hickey reducers video, was riveted, and see that they 
 are beautiful. I particularly appreciated his brief aside about how lazy 
 seqs have independent utility. 

 - In practice, for the little program I posted about previously, switching 
 to reducers involved a couple of initially unexpected complications, some 
 of now make sense to me but others of which don't fully: results of 
 reducers sometimes have to be explicitly converted, e.g. with into []; 
 r/map doesn't take multiple collections; I don't immediately see elegant 
 reducer-based approaches to uses of for or repeat, etc. 


It's true that the library does have these limitations. You could, of 
course, just implement a for-like macro that uses the reducing operations 
under the hood. Easier (but less readable) is to just convert your for into 
the equivalent r/map and r/filter operations. When using repeat, make sure 
the result sequence will be finite and call vec on it directly. Then you 
can apply the reducing functions to it and still have a foldable collection.
 

 - My initial swapping of clojure.core.reducers functions for lazy seq (and 
 agent-based parallel computing) functions seems to make my performance 
 worse rather than better. I realize that there are several possible 
 explanations for this and I have to look at my usage more carefully. It's 
 definitely possible that I'm doing more than one thing wrong, but one 
 question that this leads to is: 


Hard to say why this would be without seeing your code, but the first thing 
that comes to mind is that if you aren't calling fold or foldcat anywhere, 
then you won't be getting any parallelization from using reducers. So maybe 
your performance decrease is because you're now running single-threaded.
 

 - If I operate on a vector with a sequence of r/map and r/filter 
 operations and finally with into [] to get back a vector, then I think 
 that fold will be called within the call to into, and that parallelism 
 should happen there... right? But if that's right, how do I control the 
 group size that the call to fold uses in this case? I see how to specify 
 the group size for a direct call to fold, but not for other function in the 
 library. 


No, fold will not be called in into. The definition of into uses reduce, 
which is single-threaded. What you want is one of the following two 
formulations:

  (fold group-size cat append! foldable-collection)   ;; if you want to 
specify the group size

  (foldcat foldable-collection)  ;; if you are happy with the default 512 
group size

In both cases, the foldable-collection is just one of your (r/map f 
(r/filter p? initial-collection)) forms. For it to be foldable though, 
initial-collection needs to be a vector, map, or set (i.e., a tree-like 
collection).

I hope that helps.

  ~Gary 

 Thanks, 

  -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Timothy Baldridge
Although your original complaint was about clojure seqs being lazy. It
should be noted that reducers are also lazy down to the point of a fold or
reduce, so I'm not sure what you're really getting there. It wouldn't be
hard at all to write map, filter, remove, etc. in terms of list operations.
Perhaps that's what you're looking for? If I understand your original
complaint, you want map, filter, etc, to be eager.

On the other hand, time spent learning how lazy seqs work, and the caveats
involved will make it easier for you to understand the code produced by the
rest of the community. So perhaps that's the better option.

Timothy


On Wed, Jun 4, 2014 at 11:20 AM, Gary Johnson gwjoh...@uvm.edu wrote:

 Hey Lee, answers below. Also make sure to read my other post at 12:59pm
 today regarding the behavior of vec vs. into for reducible collections.


 On Wednesday, June 4, 2014 12:51:45 PM UTC-4, Lee wrote:

 Some quick notes and a question from my first look into this:

 - I watched a Rich Hickey reducers video, was riveted, and see that they
 are beautiful. I particularly appreciated his brief aside about how lazy
 seqs have independent utility.

 - In practice, for the little program I posted about previously,
 switching to reducers involved a couple of initially unexpected
 complications, some of now make sense to me but others of which don't
 fully: results of reducers sometimes have to be explicitly converted, e.g.
 with into []; r/map doesn't take multiple collections; I don't
 immediately see elegant reducer-based approaches to uses of for or
 repeat, etc.


 It's true that the library does have these limitations. You could, of
 course, just implement a for-like macro that uses the reducing operations
 under the hood. Easier (but less readable) is to just convert your for into
 the equivalent r/map and r/filter operations. When using repeat, make sure
 the result sequence will be finite and call vec on it directly. Then you
 can apply the reducing functions to it and still have a foldable collection.


 - My initial swapping of clojure.core.reducers functions for lazy seq
 (and agent-based parallel computing) functions seems to make my performance
 worse rather than better. I realize that there are several possible
 explanations for this and I have to look at my usage more carefully. It's
 definitely possible that I'm doing more than one thing wrong, but one
 question that this leads to is:


 Hard to say why this would be without seeing your code, but the first
 thing that comes to mind is that if you aren't calling fold or foldcat
 anywhere, then you won't be getting any parallelization from using
 reducers. So maybe your performance decrease is because you're now running
 single-threaded.


 - If I operate on a vector with a sequence of r/map and r/filter
 operations and finally with into [] to get back a vector, then I think
 that fold will be called within the call to into, and that parallelism
 should happen there... right? But if that's right, how do I control the
 group size that the call to fold uses in this case? I see how to specify
 the group size for a direct call to fold, but not for other function in the
 library.


 No, fold will not be called in into. The definition of into uses reduce,
 which is single-threaded. What you want is one of the following two
 formulations:

   (fold group-size cat append! foldable-collection)   ;; if you want to
 specify the group size

   (foldcat foldable-collection)  ;; if you are happy with the default 512
 group size

 In both cases, the foldable-collection is just one of your (r/map f
 (r/filter p? initial-collection)) forms. For it to be foldable though,
 initial-collection needs to be a vector, map, or set (i.e., a tree-like
 collection).

 I hope that helps.

   ~Gary

 Thanks,

  -Lee

  --
 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/d/optout.




-- 
“One of the main causes of the fall of the Roman Empire was that–lacking
zero–they had no way to indicate successful termination of their C
programs.”
(Robert Firth)

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

Re: non-lazy clojure?

2014-06-04 Thread Lee Spector

On Jun 4, 2014, at 1:20 PM, Gary Johnson gwjoh...@uvm.edu wrote:

 - If I operate on a vector with a sequence of r/map and r/filter operations 
 and finally with into [] to get back a vector, then I think that fold will 
 be called within the call to into, and that parallelism should happen 
 there... right? But if that's right, how do I control the group size that the 
 call to fold uses in this case? I see how to specify the group size for a 
 direct call to fold, but not for other function in the library. 
 
 
 No, fold will not be called in into. The definition of into uses reduce, 
 which is single-threaded. What you want is one of the following two 
 formulations:
 
   (fold group-size cat append! foldable-collection)   ;; if you want to 
 specify the group size
 
   (foldcat foldable-collection)  ;; if you are happy with the default 512 
 group size
 
 In both cases, the foldable-collection is just one of your (r/map f (r/filter 
 p? initial-collection)) forms. For it to be foldable though, 
 initial-collection needs to be a vector, map, or set (i.e., a tree-like 
 collection).
 
 I hope that helps.

Definitely helpful. With the fold version, and messing with the group size, I'm 
seeing much higher multicore utilization and I think I'm seeing speedups too 
(although it's a little hard to measure because of randomness). I'm also having 
new problems (pauses that get worse over time... GC?) but I'll experiment more 
before I bother anyone else with those.

Thanks so much Gary,

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-04 Thread Lee Spector

On Jun 4, 2014, at 1:29 PM, Timothy Baldridge tbaldri...@gmail.com wrote:

 Although your original complaint was about clojure seqs being lazy. It should 
 be noted that reducers are also lazy down to the point of a fold or reduce, 
 so I'm not sure what you're really getting there. It wouldn't be hard at all 
 to write map, filter, remove, etc. in terms of list operations. Perhaps 
 that's what you're looking for? If I understand your original complaint, you 
 want map, filter, etc, to be eager.
 

True, my original concern was (and my main concern still is) to avoid crashes 
(in this case, stack overflow errors) that stem from unexpected (to me) 
consequences of laziness.

Those problems could indeed be solved by rewriting map and filter etc, or 
(easier) by wringing out the laziness wherever it arises, e.g. by forcing 
everything to be a vector (or a list, as you suggest). It's a little cumbersome 
to do this everywhere, though, and I was wondering if there was an existing 
library or approach for this issue.

Then, however, it was suggested that I could have my cake and another kind of 
cake too, by using reducers. The suggestion was that this could banish the 
laziness-related issues while also providing significant performance 
improvements. Sounds good to me! I'm still working on getting my current 
project to behave well using reducers, but it seems promising.

 
 On the other hand, time spent learning how lazy seqs work, and the caveats 
 involved will make it easier for you to understand the code produced by the 
 rest of the community. So perhaps that's the better option. 

I agree that it's important to understand lazy sequences, and I do think I 
understand the core concepts reasonably well (and I've implemented lazy 
evaluation in other languages, etc.). But I've also traced some really annoying 
and hard to find bugs to unexpected (and sometimes never fully explained) 
consequences of laziness, so I'd like to find the best ways to avoid it when I 
don't really want it. If that best way turns out to make things run faster too 
then that'd be fantastic.

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-03 Thread guns
On Mon  2 Jun 2014 at 10:38:23PM -0400, Lee Spector wrote:

 PS would a call to vec do the same thing as into [] here?

IIRC vec and into [] are equivalent unless the source collection
implements IEditableCollection, in which case transients are used for a
significant performance boost.

guns


pgpL2Biq3LnWB.pgp
Description: PGP signature


Re: non-lazy clojure?

2014-06-03 Thread Mars0i
On Monday, June 2, 2014 3:32:59 PM UTC-5, Lee wrote:

 I've generally liked Clojure's pervasive laziness. It's cute and it 
 sometimes permits lovely, elegant approaches to particular programming 
 problems. 


After worrying about some bad potential problems with mutation of data 
structures (well all have to side-effect sometimes) that require some care 
with laziness--and not just my newbie oh darn I forgot that line was lazy 
problems--I had also come to feel that laziness wasn't worth its benefits.  
Cute, yeah, but the fact that I can do (take n (generate-infinite-seq)) rather 
than(generate-finite-seq n) is not helpful, and if I don't need to keep the 
whole thing in memory, then I can write a loop that doesn't.  But so much 
of what's convenient about Clojure is lazy, so it's a hard thing to avoid.

I've begun to see that laziness can really provide a modularity benefit.  
My main project is an application that generates subsequent states of a 
simulation using iterate, which generates a lazy sequence.   I can do 
things like this:

(nth
  (map write-summary-data-about-each-state-to-file
(map display-some-data-for-debugging-about-each-state 
  (generate-infinite-sequence-of-states initial-state)))
  index-of-last-state-I-care-about)

I typically don't need all information about every subsequent state; I just 
need to know a few things, and that's what those functions that are mapped 
do.  The important point is that those functions I'm mapping over the 
sequence of states are optional.  I need different ones in different 
situations.  In the first, Common Lisp version of this application, I 
embedded call to every such function into my main loop, and wrapped each 
one in an if test on a separate Boolean variable.  So I had to embed every 
call that I might possibly want to use into the main loop.  Now my main 
loop, i.e. the function that iterate calls, is only a few lines long, and 
I can add arbitrary reporting functions whenever I want using map.  None of 
that is a solution to the problems that laziness brings, but I now think 
it's possible that it's worth dealing with laziness's drawbacks.

-- 
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/d/optout.


non-lazy clojure?

2014-06-02 Thread Lee Spector

I've generally liked Clojure's pervasive laziness. It's cute and it sometimes 
permits lovely, elegant approaches to particular programming problems. And 
although I've long known that it can get you into trouble in a few unusual 
cases -- I think I recall seeing a nice blog post on issues related to 
binding and laziness by Chas Emerick several years ago -- it has generally 
seemed safe enough for most purposes. I figured that while it may not always 
provide benefits it should at least be mostly harmless.

However, after spending a couple of days tracking down a particularly confusing 
bug, and finding once again (this has happened to me at least a few times) that 
the bug was due to laziness that I never really wanted in the first place, I'm 
reconsidering.

I don't have a pared-down version of the bug that I was just dealing with (see 
below for a pointer to the non-pared-down version), but it goes away when I 
wring out all of the laziness. In this particular case I do so by calling vec 
or doall on the results of all of my calls to map and other functions function 
that produce lazy sequences (none of which I actually want to be lazy in this 
application). I was getting StackOverflowErrors and I have a half-baked 
half-theory that it was due to some bad interaction between laziness and 
garbage collection, with the JVM not realizing that it could reclaim lazy 
things that weren't yet fully evaluated. That doesn't make complete sense to 
me, and maybe that kind of bad interaction is not even possible -- I don't 
know. But I do know that all is well when I wring out all of the laziness.

One possible lesson from this experience (which was particularly frustrating, 
BTW, because the stack backtraces were particularly uninformative [1]) is that 
I should just be careful to surround any call to any function that produces a 
lazy sequence with something like vec that ensures that the laziness will be 
eliminated as soon as it is created -- except in those relatively rare cases 
where I really want the laziness. But this would be a pain, and ugly. I rely 
heavily on Clojure's sequence processing functions and I would hate to clutter 
up every call to every one of them or to have to reimplement my own non-lazy 
versions of everything.

Is there a more elegant way? Maybe somebody has already made non-lazy clones of 
all of the core functions that normally produce lazy sequences? And if so, 
maybe there's some clean way to cause my code to use the non-lazy versions 
unless specifically directed to use the lazy versions?

I realize that this is fighting with a core feature of Clojure, and that the 
idea will probably rub lots of folks the wrong way. But I've now been down this 
road a couple of times and I'm beginning to think that I'd spend less time 
tracking down mysterious bugs if I could avoid laziness more easily.

In case anybody is motivated to look into the specific bug that I was just 
dealing with, I've put the project at 
http://hampshire.edu/lspector/temp/world2D.zip. If you do lein run in its 
present state it will run fine, making little balls bounce around. (This is a 
very early version of some code that I plan to use both for teaching an AI 
class and for some ALife experiments...) But if you edit src/world2D/vec2D.clj, 
commenting out the definitions of *v, +v, and -v, and uncommenting the 
alternatives (which are the same except that they lack the calls to vec) then 
after running for a few seconds or a minute or so you'll get the stack overflow.

Thanks for any suggestions,

 -Lee


[1] The stack traces I was getting provide no information about where/what the 
offending lazy sequences are, since they don't show locals etc. I see no 
references to any of my own code, just a repeating sequence that starts:

StackOverflowError 
clojure.core/seq (core.clj:133)
clojure.core/map/fn--4211 (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
clojure.core/seq (core.clj:133)
clojure.core/map/fn--4211 (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
[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/d/optout.


Re: non-lazy clojure?

2014-06-02 Thread Softaddicts
mapv

a bit shorter :)

Luc P.

 
 I've generally liked Clojure's pervasive laziness. It's cute and it sometimes 
 permits lovely, elegant approaches to particular programming problems. And 
 although I've long known that it can get you into trouble in a few unusual 
 cases -- I think I recall seeing a nice blog post on issues related to 
 binding and laziness by Chas Emerick several years ago -- it has generally 
 seemed safe enough for most purposes. I figured that while it may not always 
 provide benefits it should at least be mostly harmless.
 
 However, after spending a couple of days tracking down a particularly 
 confusing bug, and finding once again (this has happened to me at least a few 
 times) that the bug was due to laziness that I never really wanted in the 
 first place, I'm reconsidering.
 
 I don't have a pared-down version of the bug that I was just dealing with 
 (see below for a pointer to the non-pared-down version), but it goes away 
 when I wring out all of the laziness. In this particular case I do so by 
 calling vec or doall on the results of all of my calls to map and other 
 functions function that produce lazy sequences (none of which I actually want 
 to be lazy in this application). I was getting StackOverflowErrors and I have 
 a half-baked half-theory that it was due to some bad interaction between 
 laziness and garbage collection, with the JVM not realizing that it could 
 reclaim lazy things that weren't yet fully evaluated. That doesn't make 
 complete sense to me, and maybe that kind of bad interaction is not even 
 possible -- I don't know. But I do know that all is well when I wring out all 
 of the laziness.
 
 One possible lesson from this experience (which was particularly frustrating, 
 BTW, because the stack backtraces were particularly uninformative [1]) is 
 that I should just be careful to surround any call to any function that 
 produces a lazy sequence with something like vec that ensures that the 
 laziness will be eliminated as soon as it is created -- except in those 
 relatively rare cases where I really want the laziness. But this would be a 
 pain, and ugly. I rely heavily on Clojure's sequence processing functions and 
 I would hate to clutter up every call to every one of them or to have to 
 reimplement my own non-lazy versions of everything.
 
 Is there a more elegant way? Maybe somebody has already made non-lazy clones 
 of all of the core functions that normally produce lazy sequences? And if so, 
 maybe there's some clean way to cause my code to use the non-lazy versions 
 unless specifically directed to use the lazy versions?
 
 I realize that this is fighting with a core feature of Clojure, and that the 
 idea will probably rub lots of folks the wrong way. But I've now been down 
 this road a couple of times and I'm beginning to think that I'd spend less 
 time tracking down mysterious bugs if I could avoid laziness more easily.
 
 In case anybody is motivated to look into the specific bug that I was just 
 dealing with, I've put the project at 
 http://hampshire.edu/lspector/temp/world2D.zip. If you do lein run in its 
 present state it will run fine, making little balls bounce around. (This is a 
 very early version of some code that I plan to use both for teaching an AI 
 class and for some ALife experiments...) But if you edit 
 src/world2D/vec2D.clj, commenting out the definitions of *v, +v, and -v, and 
 uncommenting the alternatives (which are the same except that they lack the 
 calls to vec) then after running for a few seconds or a minute or so you'll 
 get the stack overflow.
 
 Thanks for any suggestions,
 
  -Lee
 
 
 [1] The stack traces I was getting provide no information about where/what 
 the offending lazy sequences are, since they don't show locals etc. I see no 
 references to any of my own code, just a repeating sequence that starts:
 
 StackOverflowError 
   clojure.core/seq (core.clj:133)
   clojure.core/map/fn--4211 (core.clj:2490)
   clojure.lang.LazySeq.sval (LazySeq.java:42)
   clojure.lang.LazySeq.seq (LazySeq.java:60)
   clojure.lang.RT.seq (RT.java:484)
   clojure.core/seq (core.clj:133)
   clojure.core/map/fn--4211 (core.clj:2490)
   clojure.lang.LazySeq.sval (LazySeq.java:42)
   clojure.lang.LazySeq.seq (LazySeq.java:60)
   clojure.lang.RT.seq (RT.java:484)
 [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 

RE: non-lazy clojure?

2014-06-02 Thread Phillip Lord
Funny, I posted an article of my Clojure gotcha's today 
(http://www.russet.org.uk/blog/2991), and this is one of them. I've also had 
very nasty bugs, in addition to the general hassle of wrapping a Java API which 
works through side-effects.

I created a few functions covering the most common ones (domap, dofor and so 
on); having a comprehensive list somewhere in it's own namespace would be quite 
useful.

Phil

From: clojure@googlegroups.com [clojure@googlegroups.com] on behalf of Lee 
Spector [lspec...@hampshire.edu]
Sent: 02 June 2014 21:32
To: clojure@googlegroups.com
Subject: non-lazy clojure?

I've generally liked Clojure's pervasive laziness. It's cute and it sometimes 
permits lovely, elegant approaches to particular programming problems. And 
although I've long known that it can get you into trouble in a few unusual 
cases -- I think I recall seeing a nice blog post on issues related to 
binding and laziness by Chas Emerick several years ago -- it has generally 
seemed safe enough for most purposes. I figured that while it may not always 
provide benefits it should at least be mostly harmless.

However, after spending a couple of days tracking down a particularly confusing 
bug, and finding once again (this has happened to me at least a few times) that 
the bug was due to laziness that I never really wanted in the first place, I'm 
reconsidering.

I don't have a pared-down version of the bug that I was just dealing with (see 
below for a pointer to the non-pared-down version), but it goes away when I 
wring out all of the laziness. In this particular case I do so by calling vec 
or doall on the results of all of my calls to map and other functions function 
that produce lazy sequences (none of which I actually want to be lazy in this 
application). I was getting StackOverflowErrors and I have a half-baked 
half-theory that it was due to some bad interaction between laziness and 
garbage collection, with the JVM not realizing that it could reclaim lazy 
things that weren't yet fully evaluated. That doesn't make complete sense to 
me, and maybe that kind of bad interaction is not even possible -- I don't 
know. But I do know that all is well when I wring out all of the laziness.

One possible lesson from this experience (which was particularly frustrating, 
BTW, because the stack backtraces were particularly uninformative [1]) is that 
I should just be careful to surround any call to any function that produces a 
lazy sequence with something like vec that ensures that the laziness will be 
eliminated as soon as it is created -- except in those relatively rare cases 
where I really want the laziness. But this would be a pain, and ugly. I rely 
heavily on Clojure's sequence processing functions and I would hate to clutter 
up every call to every one of them or to have to reimplement my own non-lazy 
versions of everything.

Is there a more elegant way? Maybe somebody has already made non-lazy clones of 
all of the core functions that normally produce lazy sequences? And if so, 
maybe there's some clean way to cause my code to use the non-lazy versions 
unless specifically directed to use the lazy versions?

I realize that this is fighting with a core feature of Clojure, and that the 
idea will probably rub lots of folks the wrong way. But I've now been down this 
road a couple of times and I'm beginning to think that I'd spend less time 
tracking down mysterious bugs if I could avoid laziness more easily.

In case anybody is motivated to look into the specific bug that I was just 
dealing with, I've put the project at 
http://hampshire.edu/lspector/temp/world2D.zip. If you do lein run in its 
present state it will run fine, making little balls bounce around. (This is a 
very early version of some code that I plan to use both for teaching an AI 
class and for some ALife experiments...) But if you edit src/world2D/vec2D.clj, 
commenting out the definitions of *v, +v, and -v, and uncommenting the 
alternatives (which are the same except that they lack the calls to vec) then 
after running for a few seconds or a minute or so you'll get the stack overflow.

Thanks for any suggestions,

 -Lee


[1] The stack traces I was getting provide no information about where/what the 
offending lazy sequences are, since they don't show locals etc. I see no 
references to any of my own code, just a repeating sequence that starts:

StackOverflowError
clojure.core/seq (core.clj:133)
clojure.core/map/fn--4211 (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
clojure.core/seq (core.clj:133)
clojure.core/map/fn--4211 (core.clj:2490)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
[etc]

--
You received this message because you are subscribed

Re: non-lazy clojure?

2014-06-02 Thread Jozef Wagner
Reducers [1] provide eager variants of some core seq functions (map,
filter, etc.). Note that they do not cache the result, so they recompute it
every time you use their result.

[1] http://clojure.org/reducers


On Mon, Jun 2, 2014 at 10:32 PM, Lee Spector lspec...@hampshire.edu wrote:


 I've generally liked Clojure's pervasive laziness. It's cute and it
 sometimes permits lovely, elegant approaches to particular programming
 problems. And although I've long known that it can get you into trouble in
 a few unusual cases -- I think I recall seeing a nice blog post on issues
 related to binding and laziness by Chas Emerick several years ago -- it
 has generally seemed safe enough for most purposes. I figured that while it
 may not always provide benefits it should at least be mostly harmless.

 However, after spending a couple of days tracking down a particularly
 confusing bug, and finding once again (this has happened to me at least a
 few times) that the bug was due to laziness that I never really wanted in
 the first place, I'm reconsidering.

 I don't have a pared-down version of the bug that I was just dealing with
 (see below for a pointer to the non-pared-down version), but it goes away
 when I wring out all of the laziness. In this particular case I do so by
 calling vec or doall on the results of all of my calls to map and other
 functions function that produce lazy sequences (none of which I actually
 want to be lazy in this application). I was getting StackOverflowErrors and
 I have a half-baked half-theory that it was due to some bad interaction
 between laziness and garbage collection, with the JVM not realizing that it
 could reclaim lazy things that weren't yet fully evaluated. That doesn't
 make complete sense to me, and maybe that kind of bad interaction is not
 even possible -- I don't know. But I do know that all is well when I wring
 out all of the laziness.

 One possible lesson from this experience (which was particularly
 frustrating, BTW, because the stack backtraces were particularly
 uninformative [1]) is that I should just be careful to surround any call to
 any function that produces a lazy sequence with something like vec that
 ensures that the laziness will be eliminated as soon as it is created --
 except in those relatively rare cases where I really want the laziness. But
 this would be a pain, and ugly. I rely heavily on Clojure's sequence
 processing functions and I would hate to clutter up every call to every one
 of them or to have to reimplement my own non-lazy versions of everything.

 Is there a more elegant way? Maybe somebody has already made non-lazy
 clones of all of the core functions that normally produce lazy sequences?
 And if so, maybe there's some clean way to cause my code to use the
 non-lazy versions unless specifically directed to use the lazy versions?

 I realize that this is fighting with a core feature of Clojure, and that
 the idea will probably rub lots of folks the wrong way. But I've now been
 down this road a couple of times and I'm beginning to think that I'd spend
 less time tracking down mysterious bugs if I could avoid laziness more
 easily.

 In case anybody is motivated to look into the specific bug that I was just
 dealing with, I've put the project at
 http://hampshire.edu/lspector/temp/world2D.zip. If you do lein run in
 its present state it will run fine, making little balls bounce around.
 (This is a very early version of some code that I plan to use both for
 teaching an AI class and for some ALife experiments...) But if you edit
 src/world2D/vec2D.clj, commenting out the definitions of *v, +v, and -v,
 and uncommenting the alternatives (which are the same except that they lack
 the calls to vec) then after running for a few seconds or a minute or so
 you'll get the stack overflow.

 Thanks for any suggestions,

  -Lee


 [1] The stack traces I was getting provide no information about where/what
 the offending lazy sequences are, since they don't show locals etc. I see
 no references to any of my own code, just a repeating sequence that starts:

 StackOverflowError
 clojure.core/seq (core.clj:133)
 clojure.core/map/fn--4211 (core.clj:2490)
 clojure.lang.LazySeq.sval (LazySeq.java:42)
 clojure.lang.LazySeq.seq (LazySeq.java:60)
 clojure.lang.RT.seq (RT.java:484)
 clojure.core/seq (core.clj:133)
 clojure.core/map/fn--4211 (core.clj:2490)
 clojure.lang.LazySeq.sval (LazySeq.java:42)
 clojure.lang.LazySeq.seq (LazySeq.java:60)
 clojure.lang.RT.seq (RT.java:484)
 [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
 

Re: non-lazy clojure?

2014-06-02 Thread Lee Spector

On Jun 2, 2014, at 4:51 PM, Softaddicts lprefonta...@softaddicts.ca wrote:

 mapv
 
 a bit shorter :)
 
 Luc P.

Thanks Luc. I have indeed migrated to mapv for many cases and I could have for 
the specific final fix for the example I posted. But there are lots of other 
fixes that I made earlier in the same project, some of which may also have 
been necessary. I also used filterv in some cases... but other places it was 
messier, e.g. with for.

 -Lee 

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-02 Thread Lee Spector

On Jun 2, 2014, at 4:52 PM, Phillip Lord phillip.l...@newcastle.ac.uk wrote:

 Funny, I posted an article of my Clojure gotcha's today 
 (http://www.russet.org.uk/blog/2991), and this is one of them. I've also had 
 very nasty bugs, in addition to the general hassle of wrapping a Java API 
 which works through side-effects.
 
 I created a few functions covering the most common ones (domap, dofor and so 
 on); having a comprehensive list somewhere in it's own namespace would be 
 quite useful.
 
 Phil

It'd be nice to have a solid library of non-lazy alternatives to the core 
sequence functions.

FWIW while I've often dealt with this issue by using vectors, either with an 
explicit vec or with mapv or filterv, it's often not necessary or helpful for 
me that my sequences are actually vectors. That is, they could often be lists 
or some other kind of sequence instead. It's hard for me to know for sure, but 
I think that in many cases a doall solves the same problem that a vec 
would. This seems to be the case, for example, for the code/fix that I posted.

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-02 Thread Lee Spector

On Jun 2, 2014, at 4:52 PM, Jozef Wagner jozef.wag...@gmail.com wrote:

 Reducers [1] provide eager variants of some core seq functions (map, filter, 
 etc.). Note that they do not cache the result, so they recompute it every 
 time you use their result.
 
 [1] http://clojure.org/reducers

Thanks Josef. I haven't yet really looked into reducers, but it seems on first 
glance like it may be overkill for just dealing with this issue -- using 
fork/join and getting unneeded parallelism for routine sequence processing, 
just to ensure that one doesn't have laziness-related bugs. The recomputation 
also sounds potentially problematic.

But I do want to look into reducers for other purposes, so thanks for the 
pointer!

 -Lee

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-02 Thread Gary Johnson
Hey Lee,

  I would second Jozef's suggestion that you look into using the reducers 
library when you need non-lazy sequence operations. Although a major 
motivation of Rich's work was clearly to enable easy parallel folding via 
fork/join, the fold function is only one of many in this library. Think 
instead that the main (philosophical) purpose of reducers is to decomplect 
the reducing operation from the data representation it is acting on. And of 
course, since reduce can be used to implement (virtually) any non-lazy 
sequence operation, it stands to reason that reducers should be fully 
capable of providing new implementations of many of these functions on top 
of reduce (which it does).

  Importantly, whenever you will be chaining sequence operations together, 
reducers should be more efficient than both the lazy sequence functions 
(e.g., map, filter) and the eager vector-returning functions (e.g., mapv, 
filterv). This is because a chain of reducing functions generate no 
intermediate representations.

obligatory contrived example
  For example, let's say I wanted to sum the squares of all the even 
numbers in a sequence called samples.

  Using lazy functions: (reduce + (map #(* % %) (filter even? samples)))

  Using non-lazy functions (reduce + (mapv #(* % %) (filterv even? 
samples)))

  Using reducers (aliased as r): (reduce + (r/map #(* % %) (r/filter even? 
samples)))
/obligatory contrived example

If you need to collect the results of a sequence operation in a data 
structure rather than reducing them to an atomic value, simply use into 
rather than reduce (since into uses reduce under the hood).

So to collect the squares of all the even numbers in the samples sequence, 
just do this:

  (into [] (r/map #(* % %) (r/filter even? samples)))

As just one sample point, when I updated a statistical fire analysis 
algorithm that I wrote from using the lazy sequence functions to using the 
reducers library, I experience a full order of magnitude speedup. This sped 
up my runtime from ~6 hours to around 20 minutes. So please do yourself a 
favor and give this library a close look. It has made worlds of difference 
for some of my work.

  Good luck,
~Gary

-- 
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/d/optout.


Re: non-lazy clojure?

2014-06-02 Thread Lee Spector

Gary: That's compelling indeed, and I will look into it more!

Thanks,

 -Lee

PS would a call to vec do the same thing as into [] here?


On Jun 2, 2014, at 7:14 PM, Gary Johnson gwjoh...@uvm.edu wrote:

 Hey Lee,
 
   I would second Jozef's suggestion that you look into using the reducers 
 library when you need non-lazy sequence operations. Although a major 
 motivation of Rich's work was clearly to enable easy parallel folding via 
 fork/join, the fold function is only one of many in this library. Think 
 instead that the main (philosophical) purpose of reducers is to decomplect 
 the reducing operation from the data representation it is acting on. And of 
 course, since reduce can be used to implement (virtually) any non-lazy 
 sequence operation, it stands to reason that reducers should be fully capable 
 of providing new implementations of many of these functions on top of reduce 
 (which it does).
 
   Importantly, whenever you will be chaining sequence operations together, 
 reducers should be more efficient than both the lazy sequence functions 
 (e.g., map, filter) and the eager vector-returning functions (e.g., mapv, 
 filterv). This is because a chain of reducing functions generate no 
 intermediate representations.
 
 obligatory contrived example
   For example, let's say I wanted to sum the squares of all the even numbers 
 in a sequence called samples.
 
   Using lazy functions: (reduce + (map #(* % %) (filter even? samples)))
 
   Using non-lazy functions (reduce + (mapv #(* % %) (filterv even? samples)))
 
   Using reducers (aliased as r): (reduce + (r/map #(* % %) (r/filter even? 
 samples)))
 /obligatory contrived example
 
 If you need to collect the results of a sequence operation in a data 
 structure rather than reducing them to an atomic value, simply use into 
 rather than reduce (since into uses reduce under the hood).
 
 So to collect the squares of all the even numbers in the samples sequence, 
 just do this:
 
   (into [] (r/map #(* % %) (r/filter even? samples)))
 
 As just one sample point, when I updated a statistical fire analysis 
 algorithm that I wrote from using the lazy sequence functions to using the 
 reducers library, I experience a full order of magnitude speedup. This sped 
 up my runtime from ~6 hours to around 20 minutes. So please do yourself a 
 favor and give this library a close look. It has made worlds of difference 
 for some of my work.
 
   Good luck,
 ~Gary

-- 
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/d/optout.