Non Blocking Channel Operations in Clojure

Basic sends and receives on channels are blocking in Clojure as well. However, we can use alt! with a :default clause to implement non-blocking operations, similar to Go’s select with default.

(ns non-blocking-channel-operations
  (:require [clojure.core.async :as async :refer [chan go >! <! alt!]]))

(defn main []
  (let [messages (chan)
        signals (chan)]

    ; Here's a non-blocking receive. If a value is
    ; available on `messages` then `alt!` will take
    ; the `messages` case with that value. If not
    ; it will immediately take the `:default` case.
    (alt!
      messages ([msg] (println "received message" msg))
      :default (println "no message received"))

    ; A non-blocking send works similarly. Here `msg`
    ; cannot be sent to the `messages` channel, because
    ; the channel has no buffer and there is no receiver.
    ; Therefore the `:default` case is selected.
    (let [msg "hi"]
      (alt!
        [[messages msg]] (println "sent message" msg)
        :default (println "no message sent")))

    ; We can use multiple cases above the `:default`
    ; clause to implement a multi-way non-blocking
    ; select. Here we attempt non-blocking receives
    ; on both `messages` and `signals`.
    (alt!
      messages ([msg] (println "received message" msg))
      signals ([sig] (println "received signal" sig))
      :default (println "no activity"))))

(main)

To run this program:

$ lein run
no message received
no message sent
no activity

In Clojure, we use the core.async library to work with channels and asynchronous operations. The alt! macro is used for non-blocking operations, similar to Go’s select. The :default case in alt! serves the same purpose as the default case in Go’s select.

The structure of the program remains similar, with non-blocking receives and sends demonstrated using alt!. The multi-way non-blocking select is also implemented using alt! with multiple cases.

Note that in Clojure, we use vectors [] to represent channel operations within alt!. For sending, we use a nested vector [[channel value]], and for receiving, we use a vector with a binding form [variable-name].

This example demonstrates how Clojure’s core.async library provides similar capabilities to Go’s channel operations, allowing for non-blocking communication between concurrent processes.