Previous Table of Contents Next


10.4.2.1. A Root Package

First we introduce our structure and naming convention. By analogy with the standard library structure in which all standard packages are descendants (“children” or “grandchildren”) of a root package Ada, in this chapter all the packages are descendants of a root package HB:

    1 package HB is
    2 -- Root package for Handbook examples
    3 end HB;

This package interface provides no resources; it exists purely to serve as the root. Because this interface is empty, there is no need to provide an implementation.

Ada does not require you to use a root package. I choose to do so in conformity with a growing trend in the Ada literature, in which the provider of a given collection of packages uses a root package to provide a distinctive top-level name for all packages in the collection. In this way, the user of different collections can tell the origin of a given package; further, the likelihood of duplicated package names is minimized.

10.4.2.2. A Package for Rational-Number Arithmetic

The first package example is HB.Rationals, which provides a type and a set of programmer-defined operations on rational numbers (fractions). The Ada standard provides for integer, fixed-point, and floating-point scalar arithmetic, and Annex G, the standard numerics annex, provides for complex numbers and arithmetic. On the other hand, Ada provides no predefined support for rationals, which have many applications.

A rational value has two components: the numerator and the denominator. Here is the package interface for HB.Rationals.

    1 package HB.Rationals is
    2
    3   type Rational is private;
    4
    5   ZeroDenominator: exception;
    6
    7   function “/” (X, Y: Integer) return Rational;
    8   -- constructor:
    9   -- Pre :   X and Y are defined
   10   -- Post:   returns a rational number
   11   --   If Y > 0, returns X/Y
   12   --   If Y < 0, returns (-X)/(-Y)
   13   -- Raises: ZeroDenominator if Y = 0
   14
   15   function Numer (R : Rational) return Integer;
   16   function Denom (R : Rational) return Positive;
   17   -- selectors:
   18   -- Pre: R is defined
   19   -- Post: return numerator and denominator respectively
   20
   21   function “=” (R1, R2 : Rational) return Boolean;
   22   function “<” (R1, R2 : Rational) return Boolean;
   23   function “<=”(R1, R2 : Rational) return Boolean;
   24   function “>” (R1, R2 : Rational) return Boolean;
   25   function “>=”(R1, R2 : Rational) return Boolean;
   26   -- comparators:
   27   -- Pre : R1 and R2 are defined
   28   -- Post: return the obvious Boolean results
   29
   30   function “+”(R: Rational)   return Rational;
   31   function “-”(R: Rational)   return Rational;
   32   function “ABS”(R: Rational) return Rational;
   33   function “+”(R1, R2 : Rational) return Rational;
   34   function “-”(R1, R2 : Rational) return Rational;
   35   function “*”(R1, R2 : Rational) return Rational;
   36   function “/”(R1, R2 : Rational) return Rational;
   37   -- Pre : R, R1 and R2 are defined
   38   -- Post: return the obvious arithmetic results
   39
   40 private
   41
   42   type Rational is record
   43     Numerator  : Integer  := 0;
   44     Denominator: Positive := 1;
   45   end record;
   46
   47 end HB.Rationals;

Line 3 gives a partial view of a type Rational: The type is declared as private; its structure is not provided here. I make the type private for encapsulation purposes: The structural details of a private type are not visible to client programs, and the only operations predefined on private types are assignment and equality/inequality tests.

Using a private type gives me the flexibility to choose—and later change—the internal structure without requiring any changes to client programs. Further, I can provide a complete set of operations on the type with no worry that a client will be able to use any additional operations. This gives me control over the abstraction provided by the package.

Ada also provides for limited private types, which have no predefined operations at all, not even assignment and equality test. That is, the programmer of a package providing a limited private type is assured that a client program can do nothing with objects of the type except those operations explicitly provided in the package. This is useful in situations where, for example, it is necessary or desirable to prohibit clients from copying one object to another. The type Ada.Text_IO.File_Type is a predefined limited private type.

The full view of the private type Rational is given in the private part of the interface (lines 40-46). You see that a rational is represented as a record, that its numerator can be an arbitrary integer but that its denominator is positive, and that each rational quantity is initialized. Human readers of the package interface can obviously see this full view, but client programs cannot. A client program therefore cannot refer directly to the numerator and denominator fields but must use package-provided operations to do so.

Line 5 defines a package-specific exception ZeroDenominator; you shall see later how this is raised if a client tries to assign 0 to the denominator of a rational.

The declaration (line 7)

   function “/” (X, Y: Integer) return Rational;

declares an operator “/”, which clients can use to construct a rational. This operator allows you to write, in client programs, statements such as

   R: Rational := 1/3;


Previous Table of Contents Next