1

To try out the async library in Clojure, I translated the prime sieve example from Go. Running in the REPL, it successfully printed out the prime numbers up to 227 and then stopped. I hit Ctrl-C and tried running it again but it wouldn't print out any more numbers. Is there a way to get Clojure to handle this, or is the async library just not ready for it yet?

;; A concurrent prime sieve translated from
;; https://golang.org/doc/play/sieve.go

(require '[clojure.core.async :as async :refer [<!! >!! chan go]])

(defn generate
  [ch]
  "Sends the sequence 2, 3, 4, ... to channel 'ch'."
  (doseq [i (drop 2 (range))]
    (>!! ch i)))

(defn filter-multiples
  [in-chan out-chan prime]
  "Copies the values from 'in-chan' to 'out-chan', removing
  multiples of 'prime'."
  (while true
    ;; Receive value from 'in-chan'.
    (let [i (<!! in-chan)]
      (if (not= 0 (mod i prime))
        ;; Send 'i' to 'out-chan'.
        (>!! out-chan i)))))

(defn main
  []
  "The prime sieve: Daisy-chain filter-multiples processes."
  (let [ch (chan)]
    (go (generate ch))
    (loop [ch ch]
      (let [prime (<!! ch)]
        (println prime)
        (let [ch1 (chan)]
          (go (filter-multiples ch ch1 prime))
          (recur ch1))))))
ijt
  • 3,505
  • 1
  • 29
  • 40
  • I just looked at my own core.async code and it has lots of `<!` and `>!` but no `<<!` or `>>!`. I think it is best to send and receive between channels from within go blocks rather than outside them. – Chris Murphy Feb 20 '16 at 23:18
  • 2
    `go` is a macro. If you want to take advantage of goroutine-like behaviour in `go` blocks you must use `<!` and `>!`, and they must be visible to the `go` macro (that is you mustn't extract these operations into separate functions). – glts Feb 20 '16 at 23:35
  • This works, thank you! Can one of you post your reply as an answer so I can mark it as the solution? Here's the new version: https://gist.github.com/ijt/577fb9e11aaf8bf14e75 . – ijt Feb 21 '16 at 06:10
  • 2
    The `core.async` dispatcher runs on `42+2*CPU_CORES` threads. And the number of primes less than or equal to 227 is exactly 49. (I'm guessing you have 4 CPU cores?). This shows how you dry up all core.async threads. – ClojureMostly Feb 21 '16 at 07:23
  • Hi Andre, yes, my laptop has 4 cores. – ijt Feb 21 '16 at 08:32

1 Answers1

2

go is a macro. If you want to take advantage of goroutine-like behaviour in go blocks you must use <! and >!, and they must be visible to the go macro (that is you mustn't extract these operations into separate functions).

This literal translation of the program at https://golang.org/doc/play/sieve.go seems to work fine, also with a larger i in the main loop:

(require '[clojure.core.async :refer [<! <!! >! chan go]])

(defn go-generate [ch]
  (go (doseq [i (iterate inc 2)]
        (>! ch i))))

(defn go-filter [in out prime]
  (go (while true
        (let [i (<! in)]
          (if-not (zero? (rem i prime))
            (>! out i))))))

(defn main []
  (let [ch (chan)]
    (go-generate ch)
    (loop [i 10 ch ch]
      (if (pos? i)
        (let [prime (<!! ch)]
          (println prime)
          (let [ch1 (chan)]
            (go-filter ch ch1 prime)
            (recur (dec i) ch1)))))))
glts
  • 21,808
  • 12
  • 73
  • 94