Closing Channels in PureScript

module Main where

import Effect.Console (log)
import Effect (Effect)

-- In this example we'll use a `jobs` channel to
-- communicate work to be done from the `main` function
-- to a worker function. When we have no more jobs for
-- the worker we'll close the `jobs` channel.

main :: Effect Unit
main = do
  jobs <- newChannel
  done <- newChannel

  -- Here's the worker function. It repeatedly receives
  -- from `jobs`. In PureScript, we can't directly check
  -- if a channel is closed, so we'll use a special value
  -- to signal the end of jobs.
  let worker = do
        job <- receiveChannel jobs
        case job of
          Just j -> do
            log $ "received job " <> show j
            worker
          Nothing -> do
            log "received all jobs"
            sendChannel done true

  -- Run the worker in a separate fiber
  _ <- forkAff worker

  -- This sends 3 jobs to the worker over the `jobs`
  -- channel, then closes it.
  for_ [1, 2, 3] \j -> do
    sendChannel jobs (Just j)
    log $ "sent job " <> show j

  sendChannel jobs Nothing
  log "sent all jobs"

  -- We await the worker using the synchronization approach
  -- we saw earlier.
  _ <- receiveChannel done

  -- In PureScript, we don't have a direct way to check
  -- if a channel is closed. Instead, we can use Maybe
  -- to represent the presence or absence of a value.
  moreJobs <- receiveChannel jobs
  log $ "received more jobs: " <> show (isJust moreJobs)

-- Note: This PureScript code uses hypothetical `newChannel`,
-- `sendChannel`, and `receiveChannel` functions, which would
-- need to be implemented to match the behavior of Go's channels.

This PureScript code attempts to replicate the functionality of the original example. However, there are some important differences to note:

  1. PureScript doesn’t have built-in channels like Go does. We’re using hypothetical newChannel, sendChannel, and receiveChannel functions that would need to be implemented.

  2. Instead of using a special 2-value form of receive to check if a channel is closed, we’re using Maybe to represent the presence or absence of a value. Nothing represents the end of jobs.

  3. We’re using forkAff to run the worker in a separate fiber, which is similar to Go’s goroutines.

  4. The for_ function is used to iterate over the list of jobs, similar to the for loop in the original code.

  5. We don’t have a direct equivalent to Go’s close function for channels. Instead, we send a Nothing value to signal the end of jobs.

To run this program, you would need to compile it with the PureScript compiler and then run it with Node.js:

$ pulp build
$ node index.js
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false

This example demonstrates how to implement a similar pattern of worker and job dispatching in PureScript, using asynchronous effects and channels. The concept of closing channels is replaced with sending a special “end” value.