Rationale for Ada 2012

John Barnes
Contents   Index   References   Search   Previous   Next 

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.

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

    ARA
  AdaCore:


    AdaCore
and   Ada-Europe:

Ada-Europe