[Ada Information Clearinghouse]
Ada '83 Rationale, Sec 11.4: Overloading

"Rationale for the Design of the
Ada® Programming Language"

[Ada '83 Rationale, HTML Version]

Copyright ©1986 owned by the United States Government. All rights reserved.
Direct inquiries to the Ada Information Clearinghouse at adainfo@sw-eng.falls-church.va.us.

CHAPTER 11: General Program Structure - Visibility and Overloading

11.4 Overloading

In Ada, every use of a simple name or operator symbol is understood with reference to an (explicit or implicit) declaration of the name or symbol. In the case of types, variables, and constants, at most one such declaration can be visible at any one point in the program. In the case of subprograms, enumeration literals, and entries, however, several declarations may be simultaneously visible. An occurrence of a subprogram name, such as PUT or "*", may therefore refer to one of several simultaneously visible declarations. The name or operator symbol is then said to be overloaded.

In this section...

11.4.1 Overloading of Operators
11.4.2 Overloading of Names
11.4.3 Overloading of Literals

11.4.1 Overloading of Operators

The overloading of operators is a situation familiar also in other languages, and it illustrates the main reason for the existence of overloading in Ada. Consider, for example:

X, Y, Z :  REAL;

It is then possible to write the statements

K  :=  I*J;
Z  :=  X*Y;

in which the operator symbol "*" refers in the first statement to

    function "*" (LEFT, RIGHT :  INTEGER) return INTEGER;

and in the second statement to

    function "*" (LEFT, RIGHT :  REAL) return REAL;

The functions that implement integer multiplication and floating multiplication are represented by the same symbol because they are different implementations of the same abstract operation: the operation of multiplication.

The overloading of predefined operators has been a feature of programming languages ever since Fortran. But Ada also permits users to define new data types, for example COMPLEX or RATIONAL. Since much of the power of the language comes from its extensibility, and since proper use of that extensibility requires that we make as little distinction as possible between predefined and user-defined types, it is natural that Ada also permits new operations to be defined, by declaring new overloadings of the operator symbols. Therefore, since the operation of abstract multiplication applies to complex and rational numbers, one would expect to see

function "*" (LEFT, RIGHT :  COMPLEX)     return COMPLEX;
function "*" (LEFT, RIGHT :  RATIONAL)    return RATIONAL;

whereby the programmer can multiply rational or complex numbers using the familiar mathematical notation. The ability to coin descriptive names is an important part of good programming, and it is therefore desirable that a programming language give the programmer as much freedom as possible in the choice of names. Moreover, the use of familiar notation in new contexts is a very powerful descriptive tool: it is an example of the principle of analogy. The ability of an Ada programmer to overload operators upon new types allows the principle of analogy to be used in programming. Further examples of this principle are:

function "*" (LEFT, RIGHT :  VECTOR)            return SCALAR;
function "*" (LEFT, RIGHT :  MATRIX)            return MATRIX;

In practice, it is unlikely that two quite different overloadings, such as the two declarations of "*" above, will be defined together. It is more likely that each will be defined in its own package - in this case, one package might be called VECTOR_OPERATIONS and the other SCALAR_OPERATIONS. Similarly, rational multiplication might well be defined in a package


  type RATIONAL is private;

  function "+"   (RIGHT :  RATIONAL) return RATIONAL;
  function "-"   (RIGHT :  RATIONAL) return RATIONAL;

  function "+"                     (LEFT, RIGHT :  RATIONAL)  return RATIONAL;
  function "-"                     (LEFT, RIGHT :  RATIONAL)  return RATIONAL;
  function "*"                     (LEFT, RIGHT :  RATIONAL)  return RATIONAL;
  function "/"                     (LEFT, RIGHT :  RATIONAL)  return RATIONAL;

  function "**"                    (LEFT :  RATIONAL;  RIGHT : INTEGER) return RATIONAL;

  function "/"   (LEFT :  INTEGER;  RIGHT :  POSITIVE) return RATIONAL;

  type RATIONAL is
    end record;


In this package, a new type is defined, together with the complete set of applicable operations. The programmer defining the type is free to use the traditional operator symbols for the new type, and to give them a meaning analogous to their meaning with other types. There is no need to worry about other meanings (declarations) that might occur in other packages defining other types: the Ada overloading facility permits the package RATIONAL_ARITHMETIC to be defined as an independent software component.

These operations could be used thus:

CM_PER_INCH :  constant RATIONAL  :=  254/100;  -- by international decree!
  return INCHES * CM_PER_INCH;

Note that, by an analysis similar to the one given in section 9.2.2, we can identify the "/" operation in the expression "254/100" as being the function that takes two integers and yields a rational result.

11.4.2 Overloading of Names

Several languages beside Ada, such as Algol 68, permit operator symbols to be overloaded. Ada however also permits subprogram names to be overloaded, for exactly the same reasons. Consider for example

procedure PUT(X :  in STRING);
procedure PUT(X :  in INTEGER);

This allows a programmer to write

PUT("The value of X is:  ");

The abstract operation PUT applies indifferently to both strings and integers; it is therefore appropriate that the same name be used in both cases. Observe that this is in accord with the conventions of natural language:

    "Put the book on the shelf"

    "Put the cat out"

which does not have separate words for putting books and putting cats.

Ada does not permit the overloading of variables or constants. This again is in accordance with traditional habits of thought: we seem far more willing to accept potentially ambiguous names for operations than for things. Thus, mathematicians typically write

I1  +  I2       -- integers
X1  +  X2       -- floating-point values
V1  +  V2       -- vectors
M1  +  M2       -- matrices
Z1  +  Z2       -- complex numbers

where all the addition operations are written "+" but their operand types are distinguished by a systematic nomenclature. It seems to be a convention of our language that verbs are generic but nouns are specific; Ada reflects this by permitting operations to be overloaded but - normally - not operands. Thus, Ada allows (and we find normal)

procedure SERVE(S :  SOUP);
procedure SERVE(F :  FRUIT);        -- permitted overloading

but does not allow (and which we would find abnormal)

OF_THE_DAY :  FRUIT;              -- not a legal overloading!

11.4.3 Overloading of Literals

Literals stand for values. However, in a strongly-typed language, it must be possible to associate a type with every value, and so in some sense a literal should imply a type. This creates difficulties in two cases: first, when different values, of different types, by chance are represented by the same literal; and secondly, when the same conceptual value belongs to more than one type.

Enumeration Literals

The first case is called homography: two conceptually different values have the same symbol. It may be illustrated by

package PALETTE is
  type COLOR is (RED, ORANGE, YELLOW, GREEN,  ... );
  procedure PUT(X :  COLOR);

package BOTANY is
  type FRUIT is (APPLE, ORANGE, BANANA, KIWI,  ... );
  procedure PUT(X   FRUIT);

package ORNIT»OLOGY is
  type APTERON is (MOA, KIWI, OSTRICH,  ... );
  procedure PUT(X :  APTERON);

In no sense is a KIWI fruit the same as the flightless KIWI bird: the homography is an accident of language.

A programming language should not forbid such homography: it would be unreasonable to force the author of PALETTE to change the word ORANGE merely because it was a fruit; and indeed Ada never forbids a programmer from defining a locally unambiguous name. But it is a separate design decision whether to permit overloading of such names.

Ada permits overloading of enumeration literals; this is in accord with the idea that an enumeration literal resembles a parameterless function. Hence the following is legal:

procedure P is

Resolution is exactly as for parameterless functions: in the above declarations the required type is evident from the context.

This rule also permits character literals to be used in more than one type:

type ASCII is ( ... ,  'A',  'B',  'C',  ... );
type EBCDIC is ( ... ,  'A',  'B',  'C',  ... );

AC  :  ASCII :=  'A';
EC  :  EBCDIC    :=  'B';

Numeric Literals

The numeric literals, however, illustrate the second case. In the following:

X  :  FLOAT     :=  1.0;
Y  :  LONG_FLOAT :=  1.0;

the two occurrences of "1.0" stand for the same abstract value - unity - but in two different physical representations, and hence, in Ada, associated with two different types. It would be possible to view "1.0" as an overloaded literal - overloaded on all real types. Ada however takes a different view, that we believe corresponds more closely to our intuition. It regards real literals as being all of one type, the type universal_real, and introduces an implicit conversion to the required numeric type. The declarations above are therefore interpreted as

X  :  FLOAT     :=  FLOAT(1.0);

The alternative view - that the literals should be considered to be overloaded on all numeric types - would lead to some anomalies, of which the most annoying would perhaps be that

    if 1 < 2 then  ...

would be ambiguous: would we mean to invoke the "<" of type INTEGER or that of type LONG_INTEGER? The Ada view avoids such difficulties.

Observe by contrast that, if ASCII and EBCDIC are both visible, then

    if 'A' < '0' then  ...

will indeed be rejected as ambiguous, and rightly so, since the relation means different things in ASCII and EBCDIC.

Address any questions or comments to adainfo@sw-eng.falls-church.va.us.