Generics in Ada

Ada, like many other languages, doesn’t have built-in support for generics in the same way as the provided example. However, Ada does have its own form of generics, which we can use to demonstrate similar concepts. Here’s an Ada version that approximates the functionality:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;

procedure Generics_Example is
   -- Generic function to find the index of an element in an array
   generic
      type Element_Type is private;
      type Array_Type is array (Positive range <>) of Element_Type;
      with function "=" (Left, Right : Element_Type) return Boolean is <>;
   function Find_Index (Arr : Array_Type; Item : Element_Type) return Integer;

   function Find_Index (Arr : Array_Type; Item : Element_Type) return Integer is
   begin
      for I in Arr'Range loop
         if Arr(I) = Item then
            return I;
         end if;
      end loop;
      return -1;
   end Find_Index;

   -- Generic package for a singly-linked list
   generic
      type Element_Type is private;
   package Generic_List is
      type List is limited private;
      procedure Push (L : in out List; Item : Element_Type);
      function All_Elements (L : List) return Array_Type;
   private
      type Node;
      type Node_Access is access Node;
      type Node is record
         Value : Element_Type;
         Next  : Node_Access;
      end record;
      type List is record
         Head, Tail : Node_Access;
      end record;
   end Generic_List;

   package body Generic_List is
      procedure Push (L : in out List; Item : Element_Type) is
         New_Node : constant Node_Access := new Node'(Value => Item, Next => null);
      begin
         if L.Tail = null then
            L.Head := New_Node;
            L.Tail := New_Node;
         else
            L.Tail.Next := New_Node;
            L.Tail := New_Node;
         end if;
      end Push;

      function All_Elements (L : List) return Array_Type is
         Current : Node_Access := L.Head;
         Count   : Natural := 0;
      begin
         while Current /= null loop
            Count := Count + 1;
            Current := Current.Next;
         end loop;

         declare
            Result : Array_Type (1 .. Count);
            Index  : Positive := 1;
         begin
            Current := L.Head;
            while Current /= null loop
               Result(Index) := Current.Value;
               Index := Index + 1;
               Current := Current.Next;
            end loop;
            return Result;
         end;
      end All_Elements;
   end Generic_List;

   -- Instantiate the generic function for strings
   type String_Array is array (Positive range <>) of String (1 .. 3);
   function Find_String is new Find_Index (String, String_Array);

   -- Instantiate the generic list for integers
   package Int_List is new Generic_List (Integer);

   -- Test data
   S : constant String_Array := ("foo", "bar", "zoo");
   L : Int_List.List;

begin
   Put_Line ("index of zoo: " & Integer'Image(Find_String (S, "zoo")));

   Int_List.Push (L, 10);
   Int_List.Push (L, 13);
   Int_List.Push (L, 23);

   Put ("list: ");
   for E of Int_List.All_Elements (L) loop
      Put (Integer'Image (E) & " ");
   end loop;
   New_Line;
end Generics_Example;

This Ada program demonstrates concepts similar to those in the original Go example:

  1. We define a generic function Find_Index that can work with any array type and element type, similar to the SlicesIndex function in Go.

  2. We create a generic package Generic_List that implements a singly-linked list, which can hold elements of any type.

  3. In the main procedure, we instantiate these generics for specific types (strings and integers) and use them.

  4. We demonstrate type inference by calling the instantiated Find_String function without explicitly specifying the types.

  5. We create a list of integers, push some values onto it, and then print all elements.

To compile and run this Ada program:

$ gnatmake generics_example.adb
$ ./generics_example
index of zoo: 3
list: 10 13 23

This example showcases Ada’s approach to generics, which differs from Go’s but achieves similar functionality. Ada’s generics are compile-time constructs, which means that the compiler generates specialized code for each instantiation, potentially leading to better performance at the cost of increased compile times and code size.