Select in Idris

Our example demonstrates how to wait on multiple channel operations using a select-like construct in Idris. This is a powerful feature that combines effects and concurrency.

import Control.Concurrent
import Data.IORef
import System.Concurrency

main : IO ()
main = do
    -- For our example we'll select across two channels.
    c1 <- newIORef ""
    c2 <- newIORef ""

    -- Each channel will receive a value after some amount
    -- of time, to simulate e.g. blocking RPC operations
    -- executing in concurrent threads.
    _ <- fork $ do
        threadDelay 1000000  -- 1 second
        writeIORef c1 "one"

    _ <- fork $ do
        threadDelay 2000000  -- 2 seconds
        writeIORef c2 "two"

    -- We'll use a loop to await both of these values
    -- simultaneously, printing each one as it arrives.
    forM_ [1..2] $ \_ -> do
        selectIO [
            do msg <- readIORef c1
               when (msg /= "") $ putStrLn $ "received " ++ msg,
            do msg <- readIORef c2
               when (msg /= "") $ putStrLn $ "received " ++ msg
        ]

In this Idris version, we use IORefs to simulate channels, and fork to create concurrent threads. The selectIO function is used to wait on multiple IO actions, similar to Go’s select.

To run the program, you would compile it with the Idris compiler and then execute the resulting binary:

$ idris -o select select.idr
$ ./select
received one
received two

Note that the total execution time is only ~2 seconds since both the 1 and 2 second delays execute concurrently.

It’s important to note that Idris doesn’t have built-in channels or a direct equivalent to Go’s select statement. This example demonstrates a way to achieve similar functionality using Idris’s concurrency primitives and the selectIO function from the System.Concurrency module. The exact behavior may differ slightly from Go’s select, especially in terms of fairness and randomness of selection.