Waitgroups in PureScript

Our example demonstrates how to wait for multiple concurrent tasks to finish using the Aff monad and the parallel package in PureScript.

module Main where

import Prelude

import Effect (Effect)
import Effect.Aff (Aff, launchAff_, delay)
import Effect.Class.Console (log)
import Data.Time.Duration (Milliseconds(..))
import Control.Parallel (parallel, sequential)

-- This is the function we'll run in every Aff computation.
worker :: Int -> Aff Unit
worker id = do
  log $ "Worker " <> show id <> " starting"
  -- Sleep to simulate an expensive task.
  delay $ Milliseconds 1000.0
  log $ "Worker " <> show id <> " done"

main :: Effect Unit
main = launchAff_ do
  -- Launch several Aff computations and run them in parallel.
  sequential $ traverse parallel $ map worker [1, 2, 3, 4, 5]

In this PureScript version, we use the Aff monad to represent asynchronous computations. The worker function is defined as an Aff computation that logs its start and end, with a delay in between to simulate work.

Instead of using a WaitGroup, we use the parallel function from the Control.Parallel module to run our worker tasks concurrently. The traverse parallel combination allows us to run a list of Aff computations in parallel.

The sequential function is then used to wait for all parallel computations to complete before the main computation finishes.

To run the program, you would typically use the PureScript compiler (purs) and Node.js:

$ purs compile Main.purs
$ node -e "require('./output/Main').main()"
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

Note that this approach inherently handles error propagation through the Aff monad. If any of the workers were to throw an error, it would be propagated and the main computation would fail.

This example demonstrates how PureScript’s functional approach to concurrency differs from imperative languages, leveraging its type system and monadic structures to handle parallel computations safely and expressively.