Range Over Iterators in Ada

The original code example demonstrates the functionality of iterators and generic types in the context of managing a list structure. Since the target language is Ada, we’ll adapt these concepts to idiomatic Ada style.

Our example will use arrays for basic collection handling and demonstrate an iterative procedure to mimic the iterator pattern.

Here’s the translation to Ada, along with the necessary explanations in Markdown format suitable for Hugo:


Starting with Ada, we can define a list and push elements into it. Ada provides powerful features to manage collections and support for iteration over these collections.

First, let’s define a generic list type.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Holders;

package Generic_Linked_List is
    type Element_Type is private;
    package Element_Holder is new Ada.Containers.Indefinite_Holders (Element_Type);
    type List;
    procedure Push (L : in out List; E : Element_Type);
    procedure All (L : List; Yield : not null access procedure (E : Element_Type));
    function Gen_Fib return Iteration_Type (1);
    
private
    type Element;
    type Element_Access is access Element;
    type Element is
        record
            Value : Element_Type;
            Next : Element_Access;
        end record;
    type List is
        record
            Head : Element_Access;
            Tail : Element_Access;
        end record;
end Generic_Linked_List;

Next, we’ll implement the package body for the list and the iterator.

package body Generic_Linked_List is
    type Element_Type is Integer;  -- Define the element type as Integer
    
    procedure Push (L : in out List; E : Element_Type) is
        New_Element : Element_Access := new Element'(Value => E, Next => null);
    begin
        if L.Head = null then
            L.Head := New_Element;
            L.Tail := New_Element;
        else
            L.Tail.Next := New_Element;
            L.Tail := New_Element;
        end if;
    end Push;
    
    procedure All (L : List; Yield : not null access procedure (E : Element_Type)) is
        Current : Element_Access := L.Head;
    begin
        while Current /= null loop
            Yield (Current.Value);
            Current := Current.Next;
        end loop;
    end All;
    
    function Gen_Fib return Iteration_Type (1) is
        procedure Fibonacci (Yield : not null access procedure (E : Integer)) is
            (A, B : Integer := 1;
        begin
            loop
                Yield (A);
                (A, B) := (B, A + B);
            end loop;
        end Fibonacci;
        
        My_Iter : Iteration_Type;
    begin
        My_Iter := Fibonacci'Access;
        return My_Iter;
    end Gen_Fib;
end Generic_Linked_List;

Using the List and Fibonacci Iterator

Now, let’s use the defined list and iterate over its elements. We will also demonstrate the generation of Fibonacci numbers.

with Ada.Text_IO; use Ada.Text_IO;
with Generic_Linked_List;

procedure Main is
    package My_List is new Generic_Linked_List;
    L : My_List.List;
    procedure Print is
        new Ada.Text_IO.Put_Line (Item => Integer'Image (Item => <>));
begin
    My_List.Push (L, 10);
    My_List.Push (L, 13);
    My_List.Push (L, 23);
    
    My_List.All (L, Print'Access);
    
    Put_Line ("all: " & My_List.Collect (L)'Image);
    
    for N in My_List.Gen_Fib loop
        exit when N >= 10;
        Put_Line (Integer'Image(N));
    end loop;
end Main;

When compiling and running this Ada program:

$ gnatmake Main.adb
$ ./main
10
13
23
all: 10 13 23
1
1
2
3
5
8

Packages like Ada.Containers provide a number of useful functions to work with iterators. For example, Collect takes any iterator and collects all its values into an array or list.

Next example: Errors.