7  Strong Types

It is time to get serious about strong data types - one of the key strengths of Ada. We got a flavor of this with Strings of fixed length. In this chapter new data types which are application appropriate will be explored. In particular mechanisms to protect variables of strong data types will be studied.

7.1 Learning Objectives

  • Strong Real data types.

  • Checking values against type specifications

  • Fixed Point data types

  • Exceptions and their handling

  • Record types

7.2 Projectlet - foods

Taking advantage of Strong Typing starts with application meaningful data types.

~/bin/codemd ../Prj/hello/src/foods.adb -x StrongType -l
0014 |    -- Ref: https://en.wikipedia.org/wiki/Glycemic_index
0015 |    type Glycemic_Index is range 0 .. 100;
0016 |    subtype Low_GI is Glycemic_Index range 0 .. 55;
0017 |    subtype Medium_GI is Glycemic_Index range 56 .. 69;
0018 |    subtype High_GI is Glycemic_Index range 70 .. 100;
0019 | 
0020 |    -- https://www.verywellhealth.com/glycemic-index-chart-for-common-foods-1087476
0021 |    Apple_GI      : constant Glycemic_Index := 36;
0022 |    Banana_GI     : constant Glycemic_Index := 51;
0023 |    Chocolate_GI  : constant Glycemic_Index := 40;
0024 |    SoyaBeans_GI  : constant Glycemic_Index := 16;
0025 |    Muesli_GI     : constant Glycemic_Index := 57;
0026 |    WheatRoti_GI  : constant Glycemic_Index := 62;
0027 |    Watermelon_GI : constant Glycemic_Index := 76;
0028 |    Cornflakes_GI : constant Glycemic_Index := 81;

A base type Glycemic_Index is defined. Different ranges within this base type are sub classes. Also defined are several constants in this basic type. Similarly:

~/bin/codemd ../Prj/hello/src/foods.adb -x StrongType2 -l
~/bin/codemd ../Prj/hello/src/foods.adb -x Vars -l
0032 |    type Glycemic_Load is range 0 .. 100;
0033 |    subtype Low_GL is Glycemic_Load range 0 .. 10;
0034 |    subtype Medium_GL is Glycemic_Load range 11 .. 19;
0035 |    subtype High_GL is Glycemic_Load range 20 .. 100;
0085 |    breakfast : Glycemic_Load;
0086 |    lunch     : Glycemic_Load;
0087 |    snack     : Glycemic_Load;
0088 |    dinner    : Glycemic_Load;

And computations and assignments involving these variables:

~/bin/codemd ../Prj/hello/src/foods.adb -x Compute -l
0121 |    breakfast := Orange_GL + WhiteBread_GL;
0122 |    lunch     := breakfast + breakfast;
0123 |    snack     := Load (Watermelon_GI, 10.0);
0124 |    dinner    := Load (SoyaBeans_GI, 15.0);

However, assignments cannot be arbitrary. For example, in the following, the compiler has enough information to prevent invalid assignments and will not compile the code. No quiet promotions or conversions of constants/variable types is probably difficult in the beginning but produces reliable softtware at the end.

~/bin/codemd ../Prj/hello/src/foods.adb -x Error1 -l
~/bin/codemd ../Prj/hello/src/foods.adb -x Error2 -l
0103 | 
0104 |    -- error: invalid operand types for operator "+"
0105 |    -- error: left operand has type "Glycemic_Load" defined at line 30
0106 |    -- error: right operand has type "Glycemic_Index" defined at line 15
0107 |    -- lunch := Spaghetti_GL + Soyabeans_GI ;
0108 | 
0112 | 
0113 |    -- error: invalid operand types for operator "*"
0114 |    -- error: left operand has type "Glycemic_Load" defined at line 30
0115 |    -- error: right operand has type universal real
0116 |    -- lunch := breakfast * 2.0 ;
0117 | 

In addition, it is also very straightforward to ascertain the variable value ranges at runtime:

~/bin/codemd ../Prj/hello/src/foods.adb -x Classify -l
0069 |    procedure Classify (glname : String; gl : Glycemic_Load) is
0070 |    begin
0071 |       Annotation (glname);
0072 |       if gl in Low_GL'Range then
0073 |          Put_Line ("Low_GL");
0074 |       elsif gl in Medium_GL'Range then
0075 |          Put_Line ("Medium_GL");
0076 |       elsif gl in High_GL'Range then
0077 |          Put_Line ("High_GL");
0078 |       else
0079 |          Put_Line ("Unable to determine");
0080 |       end if;
0081 |    end Classify;

At runtime

Having defined rules of data types, can we query their attributes at runtime? How are these data types implemented?

~/bin/foods
foods
Classification of Variable Values
Orange                 : Low_GL
WhiteBread             : Low_GL
Spaghetti              : High_GL

Variable Values
breakfast              :  15
lunch                  :  30
snack                  :  8
dinner                 :  2

Implementation Details
Glycemic_Index         Size             7 bits
                       Machine_Size     8 bits
                       Range            0 ..  100

7.3 Projectlet - blood_chem

Lest we conclude that new types and subtypes have to be built on integer types :

~/bin/codemd ../Prj/hello/src/blood_chem.adb -x Dtype -l
0018 |    type Glucose_Concentration_Type is new Float range 0.0 .. 1_000.0;
0019 |    subtype Hyperglycemic_Glucose_Type is
0020 |      Glucose_Concentration_Type range 140.0 .. 1_000.0;
0021 |    subtype Hypoglycemic_Glucose_Type is
0022 |      Glucose_Concentration_Type range 0.0 .. 70.0;
0023 |    subtype Euglycemic_Glucose_Type is
0024 |      Glucose_Concentration_Type range 70.0 .. 140.0;
0025 | 
0026 |    my_glucose         : Glucose_Concentration_Type := 139.999_991;
0027 |    good_glucose_value : Euglycemic_Glucose_Type;
0028 |    hypo_value         : Hypoglycemic_Glucose_Type;
0029 |    Hyper_value        : Hyperglycemic_Glucose_Type;

It is based on the basic float data type but Glucose_Concentration_Type is a new and distinct data type implying all the variable protections. In addition, with ranges specified for subtypes which are essentially different classes, the protection is a runtime expectation.

~/bin/codemd ../Prj/hello/src/blood_chem.adb -x Exception -l
0057 |    good_glucose_value := my_glucose;
0058 |    begin
0059 |       hypo_value := good_glucose_value;
0060 |    exception
0061 |       when Event : others =>
0062 |          Put_Line ("Assigning good_glucose_value to hypo_value");
0063 |          Put_Line (Ada.Exceptions.Exception_Message (Event));
0064 |    end;

Runtime

~/bin/blood_chem
Hypoglycemia Range is glucose levels below  7.00000E+01
Hyperglycemia Range is glucose levels above  1.40000E+02
My Glucose Level is  1.40000E+02
My Glucose levels are good
Assigning good_glucose_value to hypo_value
blood_chem.adb:62 range check failed
Assigning hypo_value to hyper_value
blood_chem.adb:71 range check failed

7.4 Sample Implementation

Repository: TechAdaBook
Directory: Prj/hello