The renames facility is just one of a long list of reasons why we are big Ada fans. Here I explain how the renames facility works and how it can help you write code which is both readable and traceable.
Within the Ada community, one of the guiding principles is tracing items to their source. For example if you are referencing item Gross_Weight from Vegetable.Potato and Fruit.Apple
. then you could write:
Basket := Vegetable.Potato.Gross_Weight(PotatoCount) + Fruit.Apple.Gross_Weight(AppleCount);
This unambiguously indicates the origin of the referenced items. However with the use of meaningful labels (e.g. Exhaust_Temperature rather than ETemp) code can on occasions get somewhat verbose. The initial reaction to this is to make use of use clauses which would allow – assuming the overload can be resolved:
Basket := Gross_Weight(PotatoCount) + Gross_Weight(AppleCount);
This is however discouraged as the benefit of traceability is considered to significantly outweigh brevity. Use clauses are by common agreement “a bad thing”. The alternative is to use renames which give a shorthand method of referencing items while retaining traceability e.g.:
declare function Apple_Weight return KILOS renames Fruit.Apple.Gross_Weight; function Potato_Weight return KILOS renames Vegetable.Potato.Gross_Weight; begin Basket := Apple_Weight(AppleCount) + Potato_Weight(PotatoCount); end;
Another important principle to apply here is that renames should generally be limited to the minimum possible scope. This adheres to the traceability philosophy of the Ada community.
Another use of renames is to partially evaluate the name of an object. Consider the following code snippet:
type PERSONNEL_TYPE is record Employee_Num : EMPLOYEE_NUM_TYPE; Join_Date : DATE_TYPE; Birthday : DATE_TYPE; end record; type WORKFORCE_TYPE is array (EMPLOYEE_NUM_TYPE) of PERSONNEL_TYPE; procedure Check_Birthday ( Workers : WORKFORCE_TYPE; Today : DATE_TYPE ) is begin for I in Workers'range loop if Workers(I).Birthday.Day = Today.Day and Workers(I).Birthday.Month = Today.Month then Send_Card (Worker => Workers(I), Age => Today.Year – Workers(I).Birthday.Year); end if; end loop; end;
As can be seen the body of the loop has to restate the “Workers(I).Birthday” for each element Day/Month/Year of the record. Again a small rewrite could give:
for I in Workers'range loop declare DoB : DATE_TYPE renames Workers(I).Birthday; begin if DoB.Day = Today.Day and DoB.Month = Today.Month then Send_Card ( Worker => Workers(I), Age => Today.Year - DoB.Year ); end if; end ; end loop;
Older Ada programmers will be familiar with rename constructs such as:
with P1; function "+" (Left, Right : P1.MY_INTEGER) return P1.MY_INTEGER renames P1."+";
which were necessary to gain visibility of the operator for MY_INTEGER from package P1 in the absence of a use clause. The alternative was the extremely cumbersome:
Var := P1.”+” ( Var1, Var2);
They will also painfully remember the following error when adding extra operators in to code:
function "+" (Left, Right : P1.MY_INTEGER) return P1.MY_INTEGER renames P1."+"; function "*" (Left, Right : P1.MY_INTEGER) return P1.MY_INTEGER renames P1."+";
The multiply operator has been added by cut and paste but the rename is still of the “+” operator. Fortunately Ada 95 has pragmatically implemented the “use type” clause, thus:
with P1; use type P1.MY_INTEGER;
will give visibility to all the primitive operators associated with MY_INTEGER, while the types and objects still need disambiguation, there is no doubt where the operators are inherited from.
The rename facility of the Ada language gives a useful shorthand notation for accessing objects. Judgment must be used when to use this feature – the above examples are all fairly trivial; real world instances are likely to have more benefit. In conclusion it can be seen that renames can significantly enhance readability while at the same time retain traceability and avoid the need for use clauses.