Waitgroups in Erlang

Erlang doesn’t have a direct equivalent to Go’s WaitGroups, but we can achieve similar functionality using processes and message passing. We’ll use the spawn function to create processes (similar to goroutines) and send messages to coordinate their completion.

-module(waitgroups).
-export([main/0]).

% This is the function we'll run in every process.
worker(Id) ->
    io:format("Worker ~p starting~n", [Id]),
    % Sleep to simulate an expensive task.
    timer:sleep(1000),
    io:format("Worker ~p done~n", [Id]).

% This function will spawn workers and wait for their completion.
spawn_and_wait(N) ->
    Parent = self(),
    Pids = [spawn(fun() -> 
                      worker(I), 
                      Parent ! {done, I} 
                  end) || I <- lists:seq(1, N)],
    wait_for_workers(N).

% Wait for all workers to finish.
wait_for_workers(0) -> 
    ok;
wait_for_workers(N) ->
    receive
        {done, _Id} -> 
            wait_for_workers(N - 1)
    end.

main() ->
    % Spawn several processes and wait for their completion.
    spawn_and_wait(5).

To run the program:

$ erlc waitgroups.erl
$ erl -noshell -s waitgroups main -s init stop
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

In this Erlang version:

  1. We define a worker/1 function that simulates work by sleeping for a second.

  2. The spawn_and_wait/1 function creates N processes, each running the worker/1 function. Each process sends a message back to the parent when it’s done.

  3. The wait_for_workers/1 function uses recursion to wait for messages from all spawned processes.

  4. In the main/0 function, we call spawn_and_wait(5) to create and wait for 5 worker processes.

Note that the order of workers starting up and finishing is likely to be different for each invocation, just like in the original example.

Erlang’s built-in concurrency model based on lightweight processes and message passing provides a natural way to handle parallel tasks. While it doesn’t have an exact equivalent to Go’s WaitGroups, the message passing approach allows for flexible coordination of concurrent activities.

For more advanced use cases, you might want to look into OTP (Open Telecom Platform) behaviors like gen_server or supervisor, which provide robust patterns for building concurrent and fault-tolerant applications in Erlang.