[Ada Information Clearinghouse]
Ada '83 Quality and Style:
Guidelines for Professional Programmers
CHAPTER 5: Programming Practices
As noted in
Guideline 4.2, Ada's ability to enforce information hiding and
separation of concerns through its visibility controlling features is one of
the most important advantages of the language. Subverting these features, for
example by over liberal use of the use clause, is wasteful and dangerous.
Language Ref Manual references:
8 Visibility Rules
guideline
- Minimize using the use clause (Nissen and Wallis 1984).
- Consider using the use clause in the following situations:
- Infix operators are needed
- Standard packages are needed and no ambiguous references are introduced
- References to enumeration literals are needed
- Consider the renames clause to avoid the use clause.
- Localize the effect of all use clauses.
example
This is a modification of the example from
Guideline 4.2.3. The effect of a use
clause is localized.
...
procedure Compiler is
...
package Listing_Facilities is
end Listing_Facilities;
...
package body Listing_Facilities is separate;
...
end Compiler;
------------------------------------------------------------------------
with Text_IO;
separate (Compiler)
package body Listing_Facilities is
...
---------------------------------------------------------------------
procedure New_Line_Of_Print is
use Text_IO;
begin
...
end New_Line_Of_Print;
...
end Listing_Facilities;
rationale
These guidelines allow you to maintain a careful balance between
maintainability and readability. Excessive use of the use clause may indeed
make the code read more like prose text. However, the maintainer may also
need to resolve references and identify ambiguous operations. In the absence
of tools to resolve these references and identify the impact of changing use
clauses, fully qualified names are the best alternative.
Avoiding the use clause forces you to use fully qualified names. In large
systems, there may be many library units named in with clauses. When
corresponding use clauses accompany the with clauses and the simple names of
the library packages are omitted (as is allowed by the use clause), references
to external entities are obscured and identification of external dependencies
becomes difficult.
In some situations, the benefits of the use clause are clear. The use clause
can make several infix operators visible without the need for renames clauses.
A standard package can be used with the obvious assumption that the reader is
very familiar with those packages and that additional overloading will not be
introduced.
You can minimize the scope of the use clause by placing it in the body of a
package or subprogram or by encapsulating it in a block to restrict visibility.
notes
Avoiding the use clause completely can cause problems with enumeration
literals, which must then be fully qualified. This problem can be solved by
declaring constants with the enumeration literals as their values, except that
such constants cannot be overloaded like enumeration literals.
An argument defending the use clause can be found in Rosen (1987).
automation note
There are tools that can analyze your Ada source code, resolve overloading of
names, and automatically convert between the use clause or fully qualified
names.
Language Ref Manual references:
3.5.1 Enumeration Types,
4.5 Operators and Expression Evaluation,
8.3 Visibility,
8.4 Use Clauses,
8.5 Renaming Declarations,
C Predefined Language Environment
guideline
- Rename a long, fully qualified name to reduce the complexity if it
becomes unwieldy (Guideline 3.1.4).
- Rename declarations for visibility purposes rather than using the use
clause, especially for infix operators (Guideline 5.7.1).
- Rename parts when interfacing to reusable components originally
written with nondescriptive or inapplicable nomenclature.
- Use a project-wide standard list of abbreviations to rename common
packages.
example
procedure Disk_Write (Track_Name : in Track;
Item : in Data) renames
System_Specific.Device_Drivers.Disk_Head_Scheduler.Transmit;
rationale
If the renaming facility is abused, the code can be difficult to read. A
renames clause can substitute an abbreviation for a qualifier or long package
name locally. This can make code more readable yet anchor the code to the full
name. However, the use of renames clauses can often be avoided or made
obviously undesirable by carefully choosing names so that fully qualified
names read well. The list of renaming declarations serves as a list of
abbreviation definitions (see Guideline ). By renaming imported infix
operators, the use clause can often be avoided. The method prescribed in the
Ada Language Reference Manual (Department of Defense 1983) for renaming a type
is to use a subtype (see Guideline ). Often the parts recalled from a reuse
library do not have names that are as general as they could be or that match
the new application's naming scheme. An interface package exporting the
renamed subprograms can map to your project's nomenclature.
Language Ref Manual references:
3.3.2 Subtype Declarations,
4.5 Operators and Expression Evaluation,
8.4 Use Clauses,
8.5 Renaming Declarations
guideline
- Limit overloading to widely used subprograms that perform similar
actions on arguments of different types (Nissen and Wallis 1984).
example
function Sin (Angles : in Matrix_Of_Radians) return Matrix;
function Sin (Angles : in Vector_Of_Radians) return Vector;
function Sin (Angle : in Radians) return Small_Real;
function Sin (Angle : in Degrees) return Small_Real;
rationale
Excessive overloading can be confusing to maintainers (Nissen and Wallis 1984,
65). There is also the danger of hiding declarations if overloading becomes
habitual. Attempts to overload an operation may actually hide the original
operation if the parameter profile is not distinct. From that point on, it is
not clear whether invoking the new operation is what the programmer intended
or whether the programmer intended to invoke the hidden operation and
accidentally hid it.
note
This guideline does not prohibit subprograms with identical names declared in
different packages.
Language Ref Manual references:
6.6 Parameter and Result Type Profile - Overloading of Subprograms,
8.3 Visibility
guideline
- Preserve the conventional meaning of overloaded operators (Nissen and
Wallis 1984).
- Use "
+
" to identify adding, joining, increasing, and enhancing kinds of functions.
- Use "
-
" to identify subtraction, separation, decreasing, and depleting kinds of functions.
example
function "+" (X : in Matrix;
Y : in Matrix)
return Matrix;
...
Sum := A + B;
rationale
Subverting the conventional interpretation of operators leads to confusing
code.
note
There are potential problems with any overloading. For example, if there are
several versions of the "
+
" operator, and a change to one of them affects the
number or order of its parameters, locating the occurrences that must be
changed can be difficult.
Language Ref Manual references:
4.5 Operators and Expression Evaluation,
4.5.3 Binary Adding Operators
guideline
- Do not depend on the definition of equality provided by private types.
- When overloading the equality operator for limited private types,
maintain the properties of an algebraic equivalence relation.
rationale
The predefined equality operation provided with private types is dependent on
the data structure chosen to implement that type. If access types are used,
then equality will mean the operands have the same pointer value. If discrete
types are used, then equality will mean the operands have the same value. If
a floating point type is used, then equality is based on Ada model intervals
(see
Guideline 7.2.8.).
Any assumptions about the meaning of equality for private types will create a
dependency on the implementation of that type. See Gonzalez (1992) for a
detailed discussion.
For limited private types, the definition of "=
" is optional. When provided,
however, there is a conventional algebraic meaning implied by this symbol. As
described in Baker (1991), the following properties should remain true for the
equality operator:
-
reflexive: a = a
-
symmetric: a = b ==> b = a
-
transitive: a = b and b = c ==> a = c
Language Ref Manual references:
4.5.2 Relational Operators and Membership Tests,
6.7 Overloading of Operators,
7.4.4 Limited Types
Back to document index