Range Over Channels in Ada

with Ada.Text_IO;

procedure Range_Over_Channels is
   package IO renames Ada.Text_IO;

   type String_Access is access String;
   type String_Array is array (Positive range <>) of String_Access;

   task type Channel is
      entry Put (Item : in String);
      entry Get (Item : out String);
      entry Close;
   end Channel;

   task body Channel is
      Queue : String_Array (1 .. 2);
      Count : Natural := 0;
      Closed : Boolean := False;
   begin
      loop
         select
            when Count < Queue'Length =>
               accept Put (Item : in String) do
                  Count := Count + 1;
                  Queue (Count) := new String'(Item);
               end Put;
         or
            when Count > 0 =>
               accept Get (Item : out String) do
                  Item := Queue (1).all;
                  Queue (1 .. Count - 1) := Queue (2 .. Count);
                  Count := Count - 1;
               end Get;
         or
            accept Close do
               Closed := True;
            end Close;
         or
            terminate;
         end select;

         exit when Closed and Count = 0;
      end loop;
   end Channel;

   Ch : Channel;
begin
   -- We'll iterate over 2 values in the Channel.
   Ch.Put ("one");
   Ch.Put ("two");
   Ch.Close;

   -- This loop iterates over each element as it's
   -- received from the Channel. Because we closed the
   -- channel above, the iteration terminates after
   -- receiving the 2 elements.
   loop
      declare
         Elem : String (1 .. 3);
      begin
         select
            Ch.Get (Elem);
            IO.Put_Line (Elem);
         else
            exit;
         end select;
      end;
   end loop;
end Range_Over_Channels;

This Ada code demonstrates a similar concept to the original example, using Ada’s tasking model to simulate channels. Here’s a breakdown of the code:

  1. We define a Channel task type that can store and retrieve strings.

  2. The Channel task uses a String_Array to store the queue items, with a maximum capacity of 2.

  3. The main procedure puts two strings into the channel and then closes it.

  4. We use a loop with a select statement to iterate over the channel’s contents, which is analogous to the range loop in the original example.

  5. The loop exits when there are no more items to retrieve from the channel.

This example shows how to implement a simple producer-consumer pattern in Ada using tasks. While Ada doesn’t have built-in channels like Go, we can achieve similar functionality using tasks and protected objects.

To run this program, save it as range_over_channels.adb and compile it with an Ada compiler:

$ gnatmake range_over_channels.adb
$ ./range_over_channels
one
two

This example also demonstrates that it’s possible to close a non-empty channel (task in this case) but still have the remaining values be received.