Fork me on GitHub
#core-async
<
2015-07-29
>
meow18:07:58

If you watch any of the videos with Rob Pike talking about CSP he gives an example of using channels to calculate prime numbers using the Sieve of Eratosthenes technique, which is pretty cool. So I went looking for examples of this in Clojure and didn't find any that seemed idiomatic, particularly now that we have transducers. So I wrote one and would like some feedback since I don't have much experience with core.async. Let me know how this can be improved:

;; Sieve of Eratosthenes Prime Number Generator

(defn chan-of-primes []
  (let [ints (chan)
        primes (chan)]
    (go-loop [n 2]
      (>! ints n)
      (recur (inc n)))
    (go-loop [cur-ch ints]
      (let [prime (<! cur-ch)
            new-ch (chan 1 (filter #(not= 0 (mod % prime))))]
        (>! primes prime)
        (pipe cur-ch new-ch)
        (recur new-ch)))
    primes))

meow19:07:18

I wanted something that would return a channel with an infinite stream of prime numbers.

meow19:07:01

I also wanted it to work in both Clojure and ClojureScript.

meow19:07:33

@erik_price: no offense, but that code could be better

meow19:07:36

it creates a channel in its global namespace, has a hardcoded limit of 100 primes, and uses println inside its loop

erik_price19:07:45

I think it was just intended to demonstrate how you could use core.async to do something, not win a code quality competition. But I shouldn’t assume I know anything about what its author intended.

ghadi19:07:06

To be fair, prime calculation is a bad application for CSP... example is just an example

meow22:07:24

Prime calculation might be a bad application for CSP, but it can still be a good example for how best to write code using core.async and transducers, which, if you read my post, was my original point. So, here is what I've done to make better use of the composable nature of transducers:

(defn chan-of-ints-x [start-n xform]
  (let [ints (chan 1 xform)]
    (go-loop [n start-n]
      (>! ints n)
      (recur (inc n)))
    ints))

(defn chan-of-primes-x []
  (let [primes (chan)]
    (go-loop [cur-xf (map identity)
              cur-ch (chan-of-ints-x 2 cur-xf)]
      (let [prime  (<! cur-ch)
            new-xf (comp cur-xf (filter #(not= 0 (mod % prime))))
            new-ch (chan-of-ints-x prime new-xf)]
        (>! primes prime)
        (recur new-xf new-ch)))
    primes))

meow22:07:36

What's a good way to benchmark the two versions of the code I've posted? The latter appears to run faster, but I haven't done any benchmarking in Clojure so if anyone would like to help me with that it would be greatly appreciated. simple_smile

meow22:07:59

Here are some more thoughts that I didn't really explicitly say the first time. Clojure's core.async was influenced by golang and Rob Pike's work with channels. Rob likes to show the example of calculating primes using channels. Wouldn't it be nice to have a canonical example of the same in Clojure?