Waitgroups in Elm

In Elm, we don’t have direct equivalents to goroutines or WaitGroups. However, we can achieve similar functionality using Elm’s concurrency model, which is based on the Actor model and message passing. We’ll use Elm’s Task and Process modules to simulate concurrent operations.

import Browser
import Html exposing (Html, div, text)
import Process
import Task exposing (Task)
import Time


-- MODEL

type alias Model =
    { workersCompleted : Int
    , output : List String
    }

init : () -> (Model, Cmd Msg)
init _ =
    ( { workersCompleted = 0, output = [] }
    , Cmd.batch (List.map (startWorker << Worker) (List.range 1 5))
    )


-- UPDATE

type Msg
    = WorkerStarted Int
    | WorkerCompleted Int
    | NoOp

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        WorkerStarted id ->
            ( { model | output = model.output ++ ["Worker " ++ String.fromInt id ++ " starting"] }
            , Cmd.none
            )

        WorkerCompleted id ->
            let
                newWorkersCompleted = model.workersCompleted + 1
                newOutput = model.output ++ ["Worker " ++ String.fromInt id ++ " done"]
            in
            ( { model | workersCompleted = newWorkersCompleted, output = newOutput }
            , Cmd.none
            )

        NoOp ->
            ( model, Cmd.none )


-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none


-- VIEW

view : Model -> Html Msg
view model =
    div []
        (List.map (\line -> div [] [text line]) model.output)


-- WORKER

type Worker = Worker Int

startWorker : Worker -> Cmd Msg
startWorker (Worker id) =
    Task.perform (\_ -> WorkerStarted id) (Task.succeed ())
        |> Task.andThen (\_ -> Process.sleep 1000)
        |> Task.andThen (\_ -> Task.succeed (WorkerCompleted id))
        |> Task.perform identity


-- MAIN

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }

In this Elm version, we simulate the concept of workers and waiting for their completion:

  1. We define a Model that keeps track of the number of completed workers and the output messages.

  2. The init function starts five workers using Cmd.batch and List.map.

  3. Each worker is represented by a Task that simulates work with Process.sleep.

  4. The update function handles messages for when a worker starts and completes.

  5. The view function displays the output messages.

  6. The startWorker function creates a task that simulates a worker starting, sleeping for a second, and then completing.

This approach doesn’t use actual parallel processing, as Elm runs in a single thread. However, it demonstrates how to manage multiple asynchronous operations and wait for their completion in Elm.

To run this program, you would typically compile it to JavaScript and run it in a web browser. The output would be displayed on the web page, showing the workers starting and completing in a sequence.

Note that the order of workers starting and finishing might vary between runs due to the asynchronous nature of the tasks.