Non Blocking Channel Operations in Haskell

Basic sends and receives on channels are blocking in concurrent Haskell programs. However, we can use tryReadChan and tryWriteChan to implement non-blocking operations on channels.

import Control.Concurrent.Chan
import Control.Concurrent
import System.IO

main :: IO ()
main = do
    messages <- newChan
    signals <- newChan

    -- Here's a non-blocking receive. If a value is
    -- available on `messages` then `tryReadChan` will
    -- return `Just` that value. If not, it will
    -- return `Nothing`.
    result <- tryReadChan messages
    case result of
        Just msg -> putStrLn $ "received message " ++ msg
        Nothing  -> putStrLn "no message received"

    -- A non-blocking send works similarly. Here `msg`
    -- is attempted to be sent to the `messages` channel.
    -- `tryWriteChan` returns `True` if the write was successful,
    -- and `False` otherwise.
    let msg = "hi"
    success <- tryWriteChan messages msg
    if success
        then putStrLn $ "sent message " ++ msg
        else putStrLn "no message sent"

    -- We can use multiple `tryReadChan` calls to implement
    -- a multi-way non-blocking select. Here we attempt
    -- non-blocking receives on both `messages` and `signals`.
    resultMessages <- tryReadChan messages
    resultSignals <- tryReadChan signals
    case (resultMessages, resultSignals) of
        (Just msg, _) -> putStrLn $ "received message " ++ msg
        (_, Just sig) -> putStrLn $ "received signal " ++ show sig
        _             -> putStrLn "no activity"

To run the program:

$ runhaskell non_blocking_channel_operations.hs
no message received
no message sent
no activity

In this Haskell version:

  1. We use Control.Concurrent.Chan for channel operations.
  2. tryReadChan is used for non-blocking reads. It returns Maybe a where a is the channel’s type.
  3. tryWriteChan is used for non-blocking writes. It returns a Bool indicating success or failure.
  4. We use pattern matching and case expressions to handle the results of these operations, similar to the select statement in the original code.
  5. For the multi-way non-blocking select, we perform multiple tryReadChan operations and use pattern matching to handle all cases.

Note that Haskell’s approach to concurrency is different from some other languages. It uses lightweight threads and Software Transactional Memory (STM) for more complex concurrent operations, which can provide safer and more composable concurrency primitives.