Range Over Channels in Erlang

Here’s the Erlang translation of the “Range over Channels” example:

In a previous example, we saw how list comprehensions provide iteration over basic data structures. We can also use similar syntax to iterate over values received from a process.

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

main() ->
    % We'll iterate over 2 values in the queue process.
    {Pid, Ref} = spawn_monitor(fun() ->
        queue(["one", "two"])
    end),

    % This receive block iterates over each element as it's
    % received from the queue process. Because the process
    % terminates after sending all elements, the iteration
    % stops after receiving the 2 elements.
    receive_loop(Pid, Ref).

queue([]) ->
    ok;
queue([H|T]) ->
    receive
        {From, Ref} ->
            From ! {Ref, H},
            queue(T)
    end.

receive_loop(Pid, Ref) ->
    Pid ! {self(), make_ref()},
    receive
        {_Ref, Elem} ->
            io:format("~p~n", [Elem]),
            receive_loop(Pid, Ref);
        {'DOWN', Ref, process, Pid, _Reason} ->
            ok
    end.

To run the program:

$ erlc range_over_channels.erl
$ erl -noshell -s range_over_channels main -s init stop
one
two

This example also showed that it’s possible to terminate a process that still has remaining values to be received, but still have those remaining values be received before the receiving process terminates.

In Erlang, we don’t have channels as in some other languages, but we can achieve similar behavior using processes and message passing. The queue function acts as our channel, sending messages one at a time when requested. The receive_loop function acts as our range, receiving messages until the sending process terminates.