structadaderived-types

Assign values when deriving a record type in Ada without using discriminants


I'm trying to model three entities in Ada: Person, Woman and Man. I want Person to have one field that is Gender, that must be Unknown for Person, Male for Man and Female for Woman.

I want to implement Man and Woman as Person derived types whose Gender field is Male and Female respectively.

In addition, I want that the only allowed value for Person's Gender is Unknown, and likewise Male for Man and Female for Woman.

I have tried the following but of course it does not compile:

package Persons is

   type Genders is (Male, Female, Unknown);

   type Person is private;
   type Man is private;
   type Woman is private;

   function Get_Age    (Self : Person) return Integer;
   function Get_Name   (Self : Person) return String;
   function Get_Weight (Self : Person) return Float;
   function Get_Height (Self : Person) return Float;
   function Get_gender (Self : Person) return Genders;

private

   type Person is
      record
         Age            : Integer := 0;
         Name           : String (1..256) := (others => Character'Val(0)); -- '
         Height, Weight : Float := 0.0;
         Gender         : Genders := Unknown;
      end record;

   type Man   is new Person with Gender => Male;            
   type Woman is new Person with Gender => Female;

end Persons;

I don't want to declare Person as a parametric type because, in that way, Person would be allowed to be Male, Female or Unknown, and I don't want to allow this.

Is it possible to do what I want to to do?


Solution

  • I know you said no discriminants, but the reason you gave is to prevent assignment between them. Would you be willing to consider hiding the discriminant behind a private type? That would prevent client code from doing assignments and if you use type derivation, it would prevent you from accidentally assigning them in the package's internal code. Below are two different examples where you can hide the discriminant, preventing assignment. EDIT: Added a third option using generics.

    procedure jdoodle is
    
        package Persons1 is
    
           type Genders is (Male, Female, Unknown);
    
           type Person is private;
           type Man is private;
           type Woman is private;
    
        private
    
           type Implementation(Gender : Genders) is
              record
                 Age            : Integer := 0;
                 Name           : String (1..256) := (others => Character'Val(0)); -- '
                 Height, Weight : Float := 0.0;
              end record;
    
           type Person is new Implementation(Unknown);
           type Man    is new Implementation(Male);         
           type Woman  is new Implementation(Female);
    
        end Persons1;
    
        package Persons2 is
    
           type Genders is (Male, Female, Unknown);
    
           type Person is private;
           type Man is private;
           type Woman is private;
    
        private
    
           type Person(Gender : Genders := Unknown) is
              record
                 Age            : Integer := 0;
                 Name           : String (1..256) := (others => Character'Val(0)); -- '
                 Height, Weight : Float := 0.0;
              end record;
    
           type Man    is new Person(Male);         
           type Woman  is new Person(Female);
    
        end Persons2;
    
        package Persons3 is
    
           type Genders is (Male, Female, Unknown);
    
           type Person is private;
           type Man is private;
           type Woman is private;
    
        private
    
           generic
               The_Gender : Genders := Unknown;
           package Generic_Persons is
               type Person is record
                   Age            : Integer := 0;
                   Name           : String (1..256) := (others => Character'Val(0)); -- '
                   Height, Weight : Float := 0.0;
                   Gender         : Genders := The_Gender;
               end record;
           end Generic_Persons;
    
           package Person_Pkg is new Generic_Persons(Unknown);
           package Man_Pkg is new Generic_Persons(Male);
           package Woman_Pkg is new Generic_Persons(Female);
    
           type Person is new Person_Pkg.Person;
           type Man    is new Man_Pkg.Person;         
           type Woman  is new Woman_Pkg.Person;
    
        end Persons3;
    
    begin
        null;
    end jdoodle;