Nesting is achieved through declarative parts: A declarative part may contain bodies of program units, and each of these may in turn contain a declarative part; furthermore, a sequence of statements may contain a block statement that contains a declarative part.
A key question in the definition of program structure is that of the purpose of nesting. Clearly, nesting has been used in Algol 60 and Pascal in relation to visibility. In these languages, two units are written in the context of the same declarative part if they are to share the visibility of some common outer entities.
Is this, however, the only purpose of nesting? If it were, a logical conclusion would be the systematic unnesting of units that do not share any common visibility.
We consider this view to be too extreme. Units that do not have any visibility dependence may nevertheless be maintained together in a nested text structure for the benefit of the logical exposition of the program. There is an analogy with an encyclopedia, whose material is organized into nested subjects: It is the knowledge of this organization that enables the easy retrieval of a given subject.
Systematic unnesting of units that do not share any common visibility would produce a sequence of small units - not unlike a sequence of Fortran subprograms. Finding a given unit in such a sequence is difficult unless aided by a directory or by some convention such as alphabetical ordering. Reading the program may also be difficult since the structure of the text does not reflect the logical organization and the logical connections.
For these reasons, the ability to nest units has been retained in Ada along with the ability to control visibility that is afforded by packages and use clauses. Thus an Ada program appears as a collection of nested declarative regions. A given declarative region may include the declarations of inner program units, in which case it will also include the bodies of these program units. Each of these bodies again defines a declarative region which may in turn declare other inner program units.
In general it is possible to provide the definition of program units - especially packages - in two textually distinct parts:
We first illustrate this ability in the case of a procedure. Consider for instance the procedure declaration:
procedure PUSH(E : in ELEMENT; S : in out STACK);
This declaration contains the name of the procedure and the specification of the mode and type of each formal parameter. This is the information needed to specify the interface of PUSH, both syntactically and semantically, at least with regard to type checking. From this point of view the declaration conveys all one needs to know in order to call the procedure PUSH. The declaration could be augmented by comments specifying pre-conditions and post-conditions and any exception that might be raised by PUSH.
Obviously, however, this formulation of PUSH is incomplete in that it does not define an implementation of the procedure. The latter is provided by a procedure body:
procedure PUSH(E : in ELEMENT; S : in out STACK) is begin if S.INDEX = S.SIZE then raise STACK_OVERFLOW; else S.INDEX := S.INDEX + 1; S.SPACE(S.INDEX) := E; end if; end PUSH; |
These two constructs - the declaration and the body - jointly define the procedure. In cases where the advantages of separate specification are not essential, the procedure declaration may be omitted. In any case, the specification of the parameters must always be given in the body for reasons of readability, and also because of the possibility of overloading: there could be push procedures for items, integers, and so on.
A similar separation is provided for packages. A package declaration provides the interface to the user: the visible part. For example, the declaration of a SIMPLE_IO package is provided as follows:
package SIMPLE_IO is type FILE_NAME is limited private; procedure CREATE(FILE : out FILE_NAME); procedure READ (ELEM : out INTEGER; F : in FILE_NAME); procedure WRITE (ELEM : in INTEGER; F : in FILE_NAME); private type FILE_NAME is new INTEGER range 0 .. 50; end SIMPLE_IO; |
This declaration provides the user with the specification of the name of a type - FILE_NAME - and also with the specification of the associated procedures CREATE, READ, and WRITE. This constitutes the logical interface of the package.
The package implementation is always provided as a textually distinct package body as shown in the sketch below:
package body SIMPLE_IO is type FILE_DESCRIPTOR is record -- components of each file descriptor end record; DIRECTORY : array (FILE_NAME) of FILE_DESCRIPTOR; -- other local constants, variables and subprograms procedure CREATE(FILE : out FILE_NAME) is ... end CREATE; procedure READ(ELEM : out INTEGER; F : in FILE_NAME) is ... end READ; procedure WRITE(ELEM : in INTEGER; F : in FILE_NAME) is ... end WRITE; end SIMPLE_IO; |
As in the case of procedures, the package declaration and the package body jointly define the package considered. For pragmatic reasons (a package declaration is generally much larger than a procedure declaration), the package body does not repeat the information contained in the package declaration; furthermore packages cannot be overloaded and so there is no problem of identification.
A similar separation is also used for task units and for generic units.