Rationale for Ada 2012

John Barnes
Contents   Index   References   Search   Previous   Next 

6.6 General miscellanea

A number of improvements do not neatly fit into any other section of this book and so are lumped together here.
The first four are in fact binding interpretations and thus apply to Ada 2005 as well.
First, nominal subtypes are defined for enumeration literals and attribute references so that all names now have a nominal subtype.
This is clearly a matter for the language lawyer rather than the happy programmer. Consider the following weird example
subtype S is Integer range 1 .. 10;
case S'Last is
   when 0 =>    -- ????
This is clearly nonsense. However, Ada 2005 does not define a nominal subtype for attributes such as S'Last and so we cannot determine whether 0 is allowed as a discrete choice. The language definition is tidied up to cover such cases.
The second gap in Ada 2005 concerns intrinsic subprograms. Remember that intrinsic subprograms are functions such as "+" on the type Integer that only exist in the mind of the compiler. Clearly they have no address. The following is added to the RM:
The prefix of X'Address shall not statically denote a subprogram that has convention Intrinsic. X'Address raises Program_Error if X denotes a subprogram that has convention Intrinsic
The dynamic check is needed because of the possibility of passing an intrinsic operation as a generic parameter.
The third of these binding gems concerns the package Ada.Calendar.
The problem is that Calendar.Time is not well-defined when a time zone change occurs as for example when Daylight Saving Time is introduced or removed. Thus operations involving several time values (such as subtraction) might give the "correct" answer or might be an hour adrift. The conclusion reached was simply to admit that it is not defined so the wording is slightly changed.
Another problem with the wording in Ada 2005 is that the sign of the difference between local time and UTC as returned by UTC_Offset is not clearly defined. The sign is clarified so that for example UTC_Offset is negative in the American continent.
There is another problem with the package Calendar which will need to be addressed at some time (probably long after the author is dead). Much effort was exerted in Ada 2005 to cope with leap seconds. These arise because the angular velocity of rotation of the Earth is gradually slowing down. In earlier epochs when measurements of time were not accurate this did not matter. However, we now have atomic clocks and the slowdown is significant so that clocks are adjusted by one second as necessary and these are known as leap seconds.
But leap seconds are under threat. There is a move to suggest that tiny adjustments of one second are not worth the effort and that we should wait until the time is a whole hour wrong. A simple adjustment similar to that with which we are familiar with Daylight Saving changes is all that is needed. In other words we will have a leap hour every now and then. Indeed, if leap seconds occur about once a year as they have done on average since 1972 then a leap hour will be needed sometime in the 37th century. This will probably need to be addressed in Ada 3620 or so.
The final binding interpretation concerns class wide types and generics.
An annoyance was recently discovered concerning the use of the indefinite container packages such as
   type Index_Type is range <>;
   type Element_Type(<>) is private;
   with function "=" (Left, Right: Element_Type)
           return Boolean is <>;
package Ada.Containers.Indefinite_Vectors is
We can instantiate this with an indefinite type such as String by writing perhaps
package String_Vectors is
   new Containers.Indefinite_Vectors(Positive, String);
The third actual parameter can be omitted because the predefined operation "=" on the type String exists and does what we want.
Class wide types are another example of indefinite types. Thus we might like to create a vector container whose elements are a mixture of objects of types Circle, Square, Triangle and so on. Assuming these are all descended from the abstract type Object we want to instantiate with the class wide type Object'Class.
However, unlike String, class wide types such as Object'Class do not have a predefined equals (class wide types do not themselves have any predefined primitive operations). This is annoying since the derived types Circle, Square, and Triangle (being just records) do have a predefined equals.
So we have to write something like
function Equal(L, R: Object'Class) is
   return L = R;
end Equal;
Note that this will dispatch to the predefined equals of the type of the objects passed as parameters. They both must be of the same type of course; we cannot compare a Circle to a Triangle (anymore than we can compare Thee to a Summer's Day).
So we can now instantiate thus
package Object_Vectors is new Containers.Indefinite_Vectors(
    Positive, Object'Class, Equal);
Note irritatingly that we cannot write Equal as just "=" because this causes ambiguities.
This is all a bit annoying and so in Ada 2012, the required "=" is automatically created, we do not have to declare Equal, and the instantiation can simply be
package Object_Vectors is
   new Containers.Indefinite_Vectors(Positive, Object'Class);
This improvement is also a binding interpretation and so applies to Ada 2005 as well.
A more serious matter is the problem of the composability of equality.
In Ada 2005, tagged record types compose but untagged record types do not. If we define a new type (a record type, array type or a derived type) then equality is defined in terms of equality for its various components. However, the behaviour of components which are records is different in Ada 2005 according to whether they are tagged or not. If a component is tagged then the primitive operation is used (which might have been redefined), whereas for an untagged type, predefined equality is used even though it might have been overridden.
type Tagrec is tagged
      X1: Integer;
      X2: Integer;
   end record;
type Untagrec is
      Y1: Integer;
      Y2: Integer;
   end record;
type Index is range 0 .. 64;
function "=" (L, R: Tagrec) return Boolean is
   return L.X1 = R.X1;     --compare only first component
function "=" (L, R: Untagrec) return Boolean is
   return L.Y1 = R.Y1;     --compare only first component
function "=" (L, R: Index) return Boolean is
   raise Havoc;
   return False;
type Mixed is
      T: Tagrec;
      U: Untagrec;
      Z: Index;
   end record;
Here we have a type Mixed whose components are of a tagged record type Tagrec, an untagged record type Untagrec, and an elementary type Index. Moreover, we have redefined equality for these types.
In Ada 2005, the equality for the type Mixed uses the redefined equality for the component T but the predefined equality for U and Z. Thus it compares T.X1, U.Y1 and U.Y2 and does not raise Havoc.
In Ada 83, the predefined equality always emerged for the components of arrays and records. One reason was to avoid confusion if an inconsistency arose between "=", "<" and "<=". Remember that many elementary types and certain array types have predefined "<" as well as "=" and to get the relationship messed up would have been confusing.
However, Ada 95 introduced tagged record types and inheritance of operations became an important feature. So it seemed natural that if a structure (array or record) had components of a tagged type and equality for that tagged type had been redefined then it would be natural to expect that equality for the structure should use the redefined equality. But, fearful of introducing an incompatibility, the rule for untagged record types was left unchanged so that predefined equality re-emerges.
On reflection, this difference between tagged and untagged records was surprising and so has been changed in Ada 2012 so that all record types behave the same way and use the primitive operation. This is often called composability of equality so we can say that in Ada 2012, record types always compose for equality. Remember that this only applies to records; components which are of array types and elementary types continue to use predefined equality. So in Ada 2012, equality for Mixed only compares T.X1 and U.Y1 but not U.Y2 and still does not raise Havoc.
Concern for incompatibility and inconsistency has been allayed by a deep analysis of a number of programs. No nasties were revealed and in the only cases where it made a difference it was clear that the original behaviour was in fact wrong.
The final miscellaneum (singular of miscellanea?) concerns tags.
The package Ada.Tags defines various functions operating on tags. For example
function Parent_Tag(T: Tag) return Tag;
returns the tag of the parent unless the type has no parent in which case it returns No_Tag.
A type can be abstract or concrete. The key property of abstract types is that we cannot have an object of an abstract type. If we wish to create an object using Generic_Dispatching_Constructor and the tag passed as a parameter represents an abstract type then Tag_Error is raised. It would, of course, be far better to check whether a tag represents an abstract type before using Generic_Dispatching_Constructor.
Moreover, given a tag, there is no sensible way in Ada 2005 to find out whether it represents an abstract type. We could attempt to create an object and see if it raises Tag_Error. If it doesn't then we know that it was not abstract but we have also created an object we maybe didn't want; if it does raise Tag_Error then it might or might not have been abstract since there are other reasons for the exception being raised. Either way this is madness.
In Ada 2012, we can test the tag using the new function
function Is_Abstract(T: Tag) return Boolean;
which is added near the end of the package Ada.Tags just before the declaration of the exception Tag_Error.

Contents   Index   References   Search   Previous   Next 
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by:
The Ada Resource Association:


and   Ada-Europe: