Non Blocking Channel Operations in PureScript

Here’s the PureScript translation of the non-blocking channel operations example:

module Main where

import Prelude
import Effect (Effect)
import Effect.Console (log)
import Control.Monad.Rec.Class (forever)
import Effect.Aff (Aff, launchAff_, makeAff, nonCanceler)
import Effect.Aff.AVar (AVar)
import Effect.Aff.AVar as AVar

main :: Effect Unit
main = launchAff_ do
  messages <- AVar.empty
  signals <- AVar.empty

  -- Here's a non-blocking receive. If a value is
  -- available on `messages` then it will be taken.
  -- If not, it will immediately take the default case.
  tryReceive messages "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"
  trySend messages msg "no message sent"

  -- We can use multiple cases to implement a multi-way
  -- non-blocking select. Here we attempt non-blocking
  -- receives on both `messages` and `signals`.
  tryMultiReceive messages signals "no activity"

tryReceive :: AVar String -> String -> Aff Unit
tryReceive avar defaultMsg = do
  result <- AVar.tryTake avar
  case result of
    Just msg -> log $ "received message " <> msg
    Nothing -> log defaultMsg

trySend :: AVar String -> String -> String -> Aff Unit
trySend avar msg defaultMsg = do
  result <- AVar.tryPut msg avar
  if result
    then log $ "sent message " <> msg
    else log defaultMsg

tryMultiReceive :: AVar String -> AVar Boolean -> String -> Aff Unit
tryMultiReceive messages signals defaultMsg = do
  result <- makeAff \cb -> do
    void $ AVar.tryTake messages >>= case _ of
      Just msg -> cb (Right (Left msg))
      Nothing -> pure unit
    void $ AVar.tryTake signals >>= case _ of
      Just sig -> cb (Right (Right sig))
      Nothing -> pure unit
    cb (Right (Left defaultMsg))
    pure nonCanceler
  case result of
    Left msg -> log $ "received message " <> msg
    Right sig -> log $ "received signal " <> show sig

In this PureScript translation, we use AVar (asynchronous variables) from the purescript-aff library to simulate channels. The tryTake and tryPut functions on AVar provide non-blocking operations similar to the select statements in the original example.

The tryReceive, trySend, and tryMultiReceive functions implement the non-blocking channel operations. They use pattern matching and the Maybe type to handle the presence or absence of values, similar to the select statements in the original code.

Note that PureScript doesn’t have built-in channels or a direct equivalent to Go’s select statement. The makeAff function is used in tryMultiReceive to create a custom asynchronous effect that attempts to receive from multiple AVars, mimicking the multi-way select in the original code.

To run this program, you would need to set up a PureScript project with the necessary dependencies (purescript-aff, purescript-avar, etc.) and then use the PureScript compiler and runtime.

The output of this program would be similar to the original:

no message received
no message sent
no activity

This example demonstrates how to implement non-blocking operations and multi-way selects in PureScript, even though the language doesn’t have built-in channels like Go.