Rationale for Ada 2012
2.6 Default initial values
It is often important
that we can rely upon an object having a value within its subtype even
before it is assigned to and this especially applies in the face of type
invariants and subtype predicates. Consider a type Location
whose type invariant In_Place requires the
point to be within some place.
package Places is
type Location is private
with Type_Invariant => In_Place(Location);
function In_Place(L: Location) return Boolean;
procedure Do_It(X: in out Location; ... );
private
type Location is
record
X, Y: Float range –1.0 .. +1.0;
end record;
...
end Places;
If we just declare
an object of type Location thus
Somewhere: Location;
then there is no guarantee
that Somewhere is anywhere in particular.
If the type invariant In_Place applies and
a subprogram with an in out parameter such as Do_It
is called
Do_It(Somewhere);
then it might be that some paths through Do_It
do not assign a new value to X. Nevertheless,
on return from Do_It, the type invariant In_Place
will be checked on the parameter. If Somewhere
by chance had an accidental initial value outside the space implied by
In_Place then the call will fail. Now it might
be that other parameters of the procedure indicate to the caller that
Somewhere has not been updated in this case
but unfortunately this information is unlikely to be available to the
invariant.
One solution to this
is to ensure that objects always have an initial value satisfying the
requisite constraints, predicates or invariants. One might do this by
assigning a safe initial value thus
Somewhere: Location := (0.0, 0.0); -- illegal
but this is illegal
because the type is private. We could of course export from the package
Places a safe initial value so that we could
write
Somewhere: Location := Places.Haven;
But this is often frowned
upon because giving an explicit initial value can hide flow errors. It
is thus best to ensure that the object automatically has a safe default
value by writing perhaps
type Location is
record
X, Y: Float range –1.0 .. +1.0 := 0.0;
end record;
It is curious that
Ada allows default initial values for components of records and provides
them automatically for access types (
null) but not for scalar
types or for array types. This is remedied in Ada 2012 by the introduction
of aspects
Default_Value and
Default_Component_Value
for scalar types and arrays of scalar types respectively. The format
is as expected
type My_Float is digits 20
with Default_Value => 0.5;
type OK is new Boolean
with Default_Value => True;
The usual rule regarding the omission of =>
True does not apply in the case of Default_Value
for Boolean types for obvious reasons.
If possible, a special
value indicating the status of the default should be supplied. This particularly
applies to enumeration types. For example
type Switch is (On, Off, Unknown)
with Default_Value => Unknown;
In the case of an array
type this can be constrained or unconstrained and the default value will
apply to all components.
type Vector is array (Integer range <>) of Integer
with Default_Component_Value => 0;
Default initial values cannot be given to the predefined
types but they can be given to types derived from them such as the Boolean
type OK above.
In the case of a private type, any default has to
be given on the full type declaration.
It is important to
note that default initial values can only be given for types and not
for subtypes. If a default initial value lies outside the range of a
subtype then declaring an object of a subtype without its own specific
initial value will raise Constraint_Error.
So writing
subtype Known_Switch is Switch range On .. Off;
A_Switch: Known_Switch;
raises Constraint_Error
because the default initial value Unknown
is outside the range of the subtype Known_Switch.
If a record type is
declared and some components are given initial values but others are
not then explicitly given initial values take precedence over default
values given by these aspects. Thus if we have
type Location is
record
X: My_Float range –1.0 .. +1.0 := 0.0;
Y: My_Float range –1.0 .. +1.0;
end record;
then the component X has
default value 0.0 but component Y
has default value 0.5, (since My_Float
declared above has default value 0.5).
A final important point is that default initial values
supplied by these aspects have to be static unlike default initial values
for record components.
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by: