Range Over Channels in Clojure

In a previous example, we saw how for and range provide iteration over basic data structures. In Clojure, we can use similar concepts to iterate over values received from a channel.

(ns range-over-channels
  (:require [clojure.core.async :as async :refer [chan >! <! close! go]]))

(defn main []
  ; We'll iterate over 2 values in the `queue` channel.
  (let [queue (chan 2)]
    (>!! queue "one")
    (>!! queue "two")
    (close! queue)

    ; This `doseq` iterates over each element as it's
    ; received from `queue`. Because we closed the
    ; channel above, the iteration terminates after
    ; receiving the 2 elements.
    (doseq [elem (repeatedly #(<!! queue))]
      (println elem))))

(main)

To run the program:

$ lein run
one
two

This example also showed that it’s possible to close a non-empty channel but still have the remaining values be received.

In Clojure, we use the core.async library to work with channels. The chan function creates a channel, >!! puts a value onto a channel, <!! takes a value from a channel, and close! closes a channel.

Instead of using a for loop with range, we use doseq with repeatedly to iterate over the channel. The repeatedly function keeps calling <!! to take values from the channel until it’s closed.

Note that Clojure’s approach to channels and concurrency is somewhat different from some other languages. It provides a rich set of tools for working with asynchronous operations and concurrent programming.