Last modified on 22 March 2013, at 15:14

Ada Programming/Types/limited


Ada Lovelace 1838.jpg

Limited TypesEdit

When a type is declared limited this means that objects of the type cannot be assigned values of the same type. An Object b of limited type LT cannot be copied into an object a of same type LT.

Additionally, there is no predefined equality operation for objects of a limited type.

The desired effects of declaring a type limited include prevention of shallow copying. Also, the (unique) identity of an object is retained: once declared, a name of a variable of type LT will continue to refer to the same object.

The following example will use a rather simplifying type Boat.

     type Boat is limited private;

     function Choose
       (Load  : Sailors_Units;
        Speed : Sailors_Units)
        return  Boat;

     procedure Set_Sail (The_Boat : in out Boat);

When we declare a variable to be of type Boat, its name will denote one boat from then on. Boats will not be copied into one another.

The full view of a boat might be implemented as a record such as

     type Boat is limited record
        Max_Sail_Area : Sailors_Units;
        Max_Freight   : Sailors_Units;
        Sail_Area     : Sailors_Units;
        Freight       : Sailors_Units;
     end record;

The Choose function returns a Boat object depending on the parameters Load and Speed. If we now declare a variable of type Boat we will be better off Choosing an initial Boat (or else we might be dropping into uninitialized waters!). But when we do so, the initialization looks suspiciously like assignment which is not available with limited types:

  procedure Travel (People : Positive; Average_Speed : Sailors_Units) is

     Henrietta : Boat :=   -- assignment?
        Choose
          (Load  => People * Average_Weight * 1.5,
           Speed => Average_Speed * 1.5);

  begin
     Set_Sail (Henrietta);
  end Travel;

Fortunately, current Ada distinguishes initialization from copying. Objects of a limited type may be initialized by an initialization expression on the right of the delimiter :=.

(Just to prevent confusion: The Ada Reference Manual discriminates between assignment and assignment statement, where assignment is part of the assignment statement. An initialisation is of course an assignment which, for limited types, is done in place. An assignment statement involves copying, which is forbidden for limited types.)

Related to this feature are aggregates of limited types and “constructor functions” for limited types. Internally, the implementation of the Choose function will return a limited record. However, since the return type Boat is limited, there must be no copying anywhere. Will this work? A first attempt might be to declare a result variable local to Choose, manipulate result, and return it. The result object needs to be “transported” into the calling environment. But result is a variable local to Choose. When Choose returns, result will no longer be in scope. Therefore it looks like result must be copied but this is not permitted for limited types. There are two solutions provided by the language: extended return statements (see 6.5: Return Statements (Annotated)) and aggregates of limited types. The following body of Choose returns an aggregate of limited type Boat, after finding the initial values for its components.

     function Choose
       (Load  : Sailors_Units;
        Speed : Sailors_Units)
        return  Boat
     is
        Capacity : constant Sailors_Units := Capacity_Needed (Load);
     begin
        return Boat'
          (Max_Freight   => Capacity,
           Max_Sail_Area => Sail_Needed (Capacity),
           Freight       => Load,
           Sail_Area     => 0.0);
     end Choose;

The object that is returned is at the same time the object that is to have the returned value. The function therefore initializes Henrietta in place.

In parallel to the predefined type Ada.Finalization.Controlled, Ada provides the type Limited_Controlled in the same package. It is a limited version of the former.

Initialising Limited TypesEdit

A few methods to initialise such types are presented.

package Limited_Private_Samples is

  type Uninitialised  is limited private;
  type Preinitialised is limited private;

  type Dynamic_Initialisation is limited private;
  function Constructor (X: Integer)  -- any kind of parameters
    return Dynamic_Initialisation;

  type Needs_Constructor (<>) is limited private;
  function Constructor (X: Integer)  -- any kind of parameters
    return Needs_Constructor;

private

  type Uninitialised is record
    I: Integer;
  end record;

  type Preinitialised is record
    I: Integer := 0;  -- can also be a function call
  end record;

  type Void is null record;
  function Constructor (Object: access Dynamic_Initialisation) return Void;

  type Dynamic_Initialisation is limited record
    Hook: Void := Constructor (Dynamic_Initialisation'Access);
    Bla : Integer;  -- any needed components
  end record;

  type Needs_Constructor is record
    I: Integer;
  end record;

end Limited_Private_Samples;
package body Limited_Private_Samples is

  function Constructor (Object: access Dynamic_Initialisation) return Void is
  begin
    Object.Bla := 5;  -- may be any value only known at run time
    return (null record);
  end Constructor;

  function Constructor (X: Integer) return Dynamic_Initialisation is
  begin
    return (Hook => (null record),
            Bla  => 42);
  end Constructor;

  function Constructor (X: Integer) return Needs_Constructor is
  begin
    return (I => 42);
  end Constructor;

end Limited_Private_Samples;
 with Limited_Private_Samples;
 use  Limited_Private_Samples;
 
 procedure Try is
 
   U: Uninitialised;   -- very bad
   P: Preinitialised;  -- has initial value (good)
  
   D1: Dynamic_Initialisation;  -- has initial value (good)
   D2: Dynamic_Initialisation := Constructor (0);  -- Ada 2005 initialisation
   D3: Dynamic_Initialisation renames Constructor (0);  -- already Ada 95
 
   -- I: Needs_Constructor;  -- Illegal without initialisation
   N: Needs_Constructor := Constructor (0);  -- Ada 2005 initialisation
 
 begin
 
   null;
 
 end Try;

Note that D3 is a constant, whereas all others are variables.

Also note that the initial value that is defined for the component of Preinitialised is evaluated at the time of object creation, i.e. if an expression is used instead of the literal, the value can be run-time dependent.

X, Y: Preinitialised;

In this declaration of two objects, the initial expression will be evaluated twice and can deliver different values, because it is equivalent to the sequence[1]:

X: Preinitialised;
Y: Preinitialised;

So X is initialised before Y.

See alsoEdit

Ada 95 Reference ManualEdit

Ada 2005 Reference ManualEdit

Ada Quality and Style GuideEdit

ReferencesEdit

  1. ISO/IEC 8652:2007. "3.3.1 Object Declarations (7)". Ada 2005 Reference Manual. http://www.adaic.com/standards/05rm/html/RM-3-3-1.html. "Any declaration [...] with more than one defining_identifier is equivalent to a series of declarations each containing one defining_identifier from the list, [...] in the same order as the list."