Rationale for Ada 2012
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
generic
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
begin
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.
Consider
type Tagrec is tagged
record
X1: Integer;
X2: Integer;
end record;
type Untagrec is
record
Y1: Integer;
Y2: Integer;
end record;
type Index is range 0 .. 64;
...
function "=" (L, R: Tagrec) return Boolean is
begin
return L.X1 = R.X1; --compare only first component
end;
function "=" (L, R: Untagrec) return Boolean is
begin
return L.Y1 = R.Y1; --compare only first component
end;}
function "=" (L, R: Index) return Boolean is
begin
raise Havoc;
return False;
end;
...
type Mixed is
record
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.
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by: