Re: core.async: implementing pi.go

2015-08-03 Thread Divyansh Prakash


 does maya automatically handle operator precedence?


maya always evals from left to right. 

(maya 1 + 5 :as six, six * 2 :as twelve, twelve / 3 * 2) ;= 8


- Divyansh 

-- 
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: core.async: implementing pi.go

2015-08-02 Thread Mark Engelberg
On Sun, Aug 2, 2015 at 4:38 AM, Divyansh Prakash 
divyanshprakas...@gmail.com wrote:

 I have one more question, though: how does one work in ClojureScript
 without !! ?



This use case is a little weird because the !! is being done to block the
function until the reduction is complete, thus making pi appear externally
like a pure function.

In Clojurescript, you really don't want to block your main thread to read a
channel, because browsers are single-threaded and this will lock up your
whole page.  So in Clojurescript, you'd probably only use this channel
architecture if you wanted to compute pi, and then when it is ready, render
the answer to the webpage.  In that case, you'd just do:

(def browser-state-atom (atom {}))

(defn pi [n]
  (let [ch (to-chan (map term (range (inc n]
(go (swap! browser-state-atom assoc :pi (! (async/reduce + 0.0 ch))

and then use something like reagent to detect the change in the :pi portion
of the atom and render the result to the page.  Or you could get clever,
and render the results as they are being added up:
(defn pi [n]
  (let [ch (to-chan (map term (range (inc n]
(go-loop []
 (when-let [v (! ch)]
   (swap! browser-state-atom update :pi (fnil (partial + v)
0.0))
   (recur)

If, for some reason, you really wanted to sit and spin in a loop, blocking
for the result to complete, so you can mimic the behavior of the clojure
version, I think you could sit in a loop and use the async function take!
with the variant that calls a callback function only if a value is
immediately available on the channel.  So in that callback you mark some
variable with the final result of the reduction, and you only exit the loop
once that result is available.

-- 
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: core.async: implementing pi.go

2015-08-02 Thread Mark Engelberg
Clojure's async is built around the opinion that you, the programmer,
should be required to think about what sort of buffer you want to have on
your channel, and think about what should happen if that buffer overflows.

Your code spins off 5000 little go blocks that are each trying to write to
a channel of size 1.  This effectively forces Clojure to buffer up all
those blocking puts.  Clojure provides a fairly limited buffer (size 1024)
for the blocked puts so that you can't easily circumvent its opinionated
policy that *you* should be supplying the right size buffer with the
appropriate policy.

So, short answer: change your (chan) to something like (chan 1) and
your code works fine.

But that's probably not what you're looking for.  We really don't want to
have to size the buffer based on the input to the pi function.  We want to
be able to deal with arbitrarily large inputs with some reasonably smallish
buffer.  Because this is a toy problem, it glosses over something that
would be critical for us to think about in a real application: Which runs
faster, the term computation or the additions in the accumulator?  The
answer to that question leads to two a couple different solutions.

Before I show some possible solutions, I should mention that I'm not a big
fan of the way you wrote the term function.  I would argue that it is
much cleaner to write it as a pure function (something you can test).
Don't inject notions of channels into a pure arithmetic computation.  Also,
you've made the math unnecessarily convoluted with your overuse of -.
Embrace infix math.  If you think the -1^k expression is awkward, you could
do something like this:

(defn term [k]
  (/ (if (even? k) 4 -4)
 (inc (* 2 k

There, a nice clean pure function that makes it clear that -1^k is just a
trick for putting the correct sign on the expression.  You can change 2
to 2.0 if you want to return a double rather than a rational number from
this function (or use this version and just coerce to the double in the
consumer, depending on the behavior you want).

Or, if you want to use pow:
(defn term [k]
  (/ (* 4 (Math/pow -1 k))
 (inc (* 2 k

Or, you can leverage the fact that multiplying by 1 or -1 is the same as
dividing and eliminate the multiplication:
(defn term [k]
  (/ 4 (Math/pow -1 k)
 (inc (* 2 k

Any of these would be preferable to the - version which is harder to read.

Now, back to the key question.

Case 1: term is fast, addition is slow
In this case, we really just need one thread to generate all the term
expressions, and we simply want to let the producer get ahead of the
consumer by some bounded amount.  So something like this would work:

(defn pi [n]
  (let [ch (to-chan (map term (range (inc n]
(!! (async/reduce + 0.0 ch

to-chan uses a bounded buffer of size 100, and we're also making use of the
way map works with chunked sequences to efficiently do fast operations.  If
you don't want chunked sequence behavior, you can use `sequence` with a
transducer:

(defn pi [n]
  (let [ch (to-chan (sequence (map term) (range (inc n]
(!! (async/reduce + 0.0 ch

Case 2: term is slow, addition is fast
The above code will still work if term is slower than addition, but if we
know term is slower, we can improve performance by running the (map term)
transducer on multiple processors.  async's pipeline lets you do just
that.  Let's say we want to use 20 go blocks.  We'll put all the numbers
0 through n on one channel, pipe it to another channel with the (map term)
transducer using a parallelism count of 20, and reduce the result:

(defn pi [n]
  (let [ch (chan 100)]
(pipeline 20 ch (map term) (to-chan (range (inc n
(!! (async/reduce + 0.0 ch

Of course, you can get computational parallelism without using channels at
all:
(defn pi [n]
  (clojure.core.reducers/fold + (clojure.core.reducers/map term (vec (range
(inc n))

which runs several times faster (depending on your number of processors)
than the naive:
(defn pi [n]
  (reduce + (map term (range (inc n)



Summary: core.async forces you to make some choices, because of its
philosophy that unlimited buffers results in error-prone programs that will
crash unexpectedly when resources are exceeded, but once you make those
choices, it gives you some nice high-level constructs to express these
ideas succinctly.

-- 
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: core.async: implementing pi.go

2015-08-02 Thread Divyansh Prakash
Hey, puzzler!
Thanks for the detailed response. Just changing (chan) to (chan n) actually 
worked!
 
I get your displeasure with how 'term' is implemented. 
That's not how I generally code. I'm very new to core.async and was aiming 
for a direct translation of the Go code.

I do get a little carried away with - at times, though.

Also - though optimization wasn't a priority, thanks for taking out the 
time to show alternate approaches along with their use cases. 
I haven't tried reducers yet, so that was new.

I have one more question, though: how does one work in ClojureScript 
without !! ?

- Divyansh

-- 
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: core.async: implementing pi.go

2015-08-02 Thread Divyansh Prakash
Makes sense.
By the way - I've refactored my code 
https://github.com/divs1210/go-play/blob/master/src/go_play/pi.clj to not 
use a go block inside 'term', and made it much more readable (IMO) than 
before using my maya library https://github.com/divs1210/maya.
I'm telling you this because I'm not a big fan of writing mathematical 
expressions as s-exps but you seem all for it.
I had written the macros defined in maya quite a long time ago, but you 
just inspired me to push it to clojars, so thanks for that! :)

- Divyansh

-- 
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: core.async: implementing pi.go

2015-08-02 Thread Mark Engelberg
I agree that there's value to making math expressions look like the way
they are written in actual math -- it is much easier to tell at a glance
that you've entered the correct formula.  I think your maya library is a
nice contribution.  (I think Incanter has a similar macro, but it is nice
to have it in a lightweight standalone library.
https://github.com/joyofclojure/unfix is another one, I haven't figured out
exactly how they all compare.  It's not obvious to me from your
documentation -- does maya automatically handle operator precedence?).

My point is mainly that people who read and write Clojure are used to using
infix notation, so for that particular audience, it should be no big deal
to read the infix math unless the expression is particularly complex.
Specifically, I don't think that reordering math with - gains you any
readability.  But I'm all for judicious use of infix DSLs.



On Sun, Aug 2, 2015 at 5:59 AM, Divyansh Prakash 
divyanshprakas...@gmail.com wrote:

 Makes sense.
 By the way - I've refactored my code
 https://github.com/divs1210/go-play/blob/master/src/go_play/pi.clj to
 not use a go block inside 'term', and made it much more readable (IMO) than
 before using my maya library https://github.com/divs1210/maya.
 I'm telling you this because I'm not a big fan of writing mathematical
 expressions as s-exps but you seem all for it.
 I had written the macros defined in maya quite a long time ago, but you
 just inspired me to push it to clojars, so thanks for that! :)

 - Divyansh

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


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