6.5. Subtypes: constraints on types - JulTob/Ada GitHub Wiki

When a new type is defined the compiler will treat it as a distinct entity.

If we want a constraint of an existing type, instead of a different type, we use subtype.

Subtypes can be defined dynamically.

It’s just a name for a set of values. They can be mixed with variables of the parent type. But if a variable of the subtype tries to get a value out of range a Constrain_Error is raised by a run-time check.


subtype Name is Father_Type;

subtype Name is Father_Type range lower .. upper;

subtype Weekday_Type is Day_type range Mon .. Fri;

subtype Upper_Chars is Character range 'A' .. 'Z';

I : Integer range 1..10  -- Implicit subtype


--  These two subtypes are defined in the standard, and always visible:

subtype Natural  is Integer range 0 .. Integer’Last;
subtype Positive is Integer range 1 .. Integer’Last;

Subtypes are compatible with their base types . They can be placed in the same place as any variable of the base type can. Also variables of different subtypes that are derived from the same base type are compatible.

procedure Demonstrate_Subtypes 
   (Lower, Upper : in Day_Type; 
    Day: in out Day_Type)
 is 
   subtype Interval is Day_Type range Lower .. Upper;
   X: Interval := Interval’First;
 begin
   Day:= X; --No runtime checks. Will work
   X:= Day; --Run time check. Day might be out of range
   End Demonstrate_Subtypes 

New Type VS Subtype

type Salary is new float;
type SmallSalary is new Salary range 0.0 .. 35_000.0;

When you create a new type, the type is considered to be incompatible with the type it is derived from. If you want to add a small salary to a salary, you'll have to use type casting, even though they are both floats.

  totalSalary, BigSalary : Salary;
  mySalary : SmallSalary;
  ...
  totalSalary := bigSalary + Salary( mySalary );

To type cast one type into another, use the type name and the value to convert in parantheses after it.

Use subtype to create a type a SmallSalary that is compatible with a Salary.

  subtype SmallSalary is Salary range 0..35_000.0;
  mySalary : SmallSalary;
...
    totalSalary := bigSalary + mySalary ;

Subtype can also be used to rename types.

  subtype sb is SalaryBonus;

Is good practice to determine the types that may be changed in future

  subtype Number is Integer;

Subtypes as ranges

The use of subtypes is also useful to catch elements out of ranges. This is, we may have imported a float type, but we have a system that only behaves well in a subrange of this type. By building a subtype, if the values we work with exceed the range of tolerance we will raise an error without the need of extra safeguards.

'''Ada type Frequency is digits 12; subtype WorkingFrequency is Frequency range 0.0 .. 10_000.0; subtype OptimalFrequency is WorkingFrequency range 200.0 .. 1000.0; f: Frequency; wf: WorkingFrequency; of: OptimalFrequency; ''' In the above example, we can assign a value of the subrange to a general variable, and vice-versa. But if the value of a subtype variable is outside the range, it will ALLWAYS rise an exception.

f := 2.0 * wf   -- The calculation will not exceed the range
f := 10.0 * of  -- A subtype of a subtype is also a subtype. So it works fine.
wf := f * 0.5   -- The operation is legal, but the value may exceed the range, raising an error.
wf := 2.0 * of  -- The operation is legal. Will behave nicely inside the  WorkingFrequency range.
of := f * 0.01  -- The operation is legal. Will behave nicely inside the  OptimalFrequency range. 
of := wf * 0.1  -- The operation is legal. Will behave nicely inside the  OptimalFrequency range. 

In any case, if the value assigned is outside the boundaries of the type or subtype it will raise a Constrain Error.