Is it possible to do this with expression functions?

Im studying how far I can go using expression functions to have everything defined in specification package.

My goal is simple, I have an enumeration that defines a set of 5 possible contents

   type Enum is (A1, A2, A3, A4, A5);

I define a possible specific content implementation where only 2 of 5 are active:

   type TableType is array (Natural range <>) of Natural;

   Table : constant TableType :=
     (1 => A3,
      2 => A5
     );

From someone that call this package with Enum as argument I just can make a loop in the table to find the element of enum. But I want to make a lookup table that maps directly for save CPU.

I start with a lookup table where is possible to know wich element of the enumeration is active in this implementation. I was successfull with this expression function:

   type WhoisactiveType is array (Enum) of boolean;
   function isActive (elem : Enum) return boolean is
     (for some I in Table'Range => (elem = Table(I)));
   WhoisActive : WhoisactiveType := [ for I in Enum'Range => (isActive (I))];

But I didnt find the way to make the lookup table to know which internal index of the table are used for every element of the enum. I’v tried without success:

   function Convert (elem : Enum) return Natural is
     (for I in Table'Range => (if elem = Table(I) then I else 0));
 Map : MapConvertionType := [for I in Enum'Range => (Convert (I))];

Of course, I was able to do all of this in the body. But the goal is to try to do in the specification package. For the body I just did this:

   function Convert (elem : Enum) return Natural is
   begin
      for I in Table'Range loop
         if table(I) = elem then
            return I;
         end if;
      end loop;
      return 0;
   end Convert;

   procedure InitMap is
   begin
      for I in Map'Range loop
         Map(I) := Convert(I);
      end loop;
   end InitMap;

¿Does anybody knows how to build this simple lookup table in the specification package?

PD: I also tried to make Map expression function that work like this:

   function Map (elem: Enum) return Natural is
     (case elem is
         when A3 =>1,
         when A5 =>2,
      when others =>0
     );

But the this solution requires two parallel configurations for the same thing (Table variable content and Map function content) that could drive to possible mistakes.

I would have started out reversing your table logic and use the enumeration as the index to a set of booleans:

Who_Is_Active: array(Enum) of Boolean := 
   (A3     => True,
    A5     => True,
    others => False);

if you want to store other data you can do:

type Optional(Valid : Boolean := False) is record
   case Valid is
      when False => null;
      when True  => Index : Positive;  -- Some index to another table
   end case;
end record;

Who_Is_Active: array(Enum) of Optional:= 
   (A3     => (Valid => True, Index => 1),
    A5     => (Valid => True, Index => 2),
    others => (Valid => False);

if you absolutely wanted to stick with your method, then one way is to use a reduction expression

pragma Ada_2022;

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps;

procedure jdoodle is

    type Enum is (A1, A2, A3, A4, A5);
    
    type Table_Type is array(Positive range <>) of Enum;
         
    Table : constant Table_Type := [1 => A3, 2 => A5];
    
    type WhoisactiveType is array (Enum) of Boolean;

    function isActive (Elem : Enum) return Boolean is
        ([for Item of Table => (Item = Elem)]'Reduce("or",False));  -- Reduction expression
        
    WhoisActive : WhoisactiveType := [ for I in Enum'Range => (isActive (I))];
    
begin
    for Item of WhoIsActive loop
        Put_Line(Item'Image);
    end loop;
end jdoodle;

Output:

gcc -c jdoodle.adb
gnatbind -x jdoodle.ali
gnatlink jdoodle.ali -o jdoodle
FALSE
FALSE
TRUE
FALSE
TRUE

The isActive function in this example iterates through Table, compares the current element of the table to Elum and logically "or"s all of the results together (with a starting result of False). So if one True comparison result is found, then the final result is True.

There’s probably other methods, but that is the first one I thought of.

Side note: There’s no need to avoid a body file here, you can still have it premade and just have a function return the results of the table:

    package P1 is
        function Map(Elem : Enum) return Natural with Inline;
    end P1;
    
    package body P1 is 
    
        function Convert (elem : Enum) return Natural is
        begin
            for I in Table'Range loop
                if table(I) = elem then
                    return I;
                end if;
            end loop;
            return 0;
        end Convert;
        
        type MapConversionType is array(Enum) of Natural;
        Internal_Map : constant MapConversionType := [for I in Enum'Range => (Convert (I))];
        
        function Map(Elem : Enum) return Natural is (Internal_Map(Elem));
        
    end P1;

The table is premade here even though it is in the body. That said, I still think just making a table indexed by enums from the start is the better way to go.

Thank you @jere. Your answer was really inspiring.
I think I will go with you first proporsal.

I think you can avoid the reduction expression with a (for some E of T => Test_On_E (E))