-*- Mode: Text; Fonts: Ct18b -*- Chapter 13.1.1: PACKAGE SPECIFICATIONS A package specification takes the form: package SOME_NAME is . . . end SOME_NAME; A package specification may itself be further divided into two parts, the visible part and the private part. The visible part declares the resources that may be used outside the package; the package is said to export these entities. A package may export any of a number of items, including objects, types, subtypes, subprograms, tasks, numbers, exceptions, constants, renamed entities, and even other packages. It is good practice to keep package specifications small and to export only a single logical chunk. Later in this chapter, we shall discuss how this grouping may best be chosen. The private part appears only at the end of the package specification and is introduced by the reserved work private. The private part is textually available to a package user and, like the visible part, consists of declarative items. However, the private part cannot be referenced outside the package. It exists in the specification part to support the separate compilation unit mechanism. Its use will be explained later in a section on private types. A program unit may use the resources of any package that is visible. For example, consider the package COMPLEX introduced in Chapter 4: package COMPLEX is type NUMBER is record REAL_PART : FLOAT; IMAGINARY_PART : FLOAT; end record; function "+" (A,B : in NUMBER) return NUMBER; function "-" (A,B : in NUMBER) return NUMBER; function "*" (A,B : in NUMBER) return NUMBER; end COMPLEX; Package COMPLEX is visible to another program unit P if it is declared within or in an outer scope of P and is not hidden by another declaration. There are more detailed scope rules, and these are discussed in Chapter 20. For an example of the general case, we have: procedure MAIN_PROGRAM is procedure FIRST is . . . end FIRST; --start of the declarative part package COMPLEX is . . . end COMPLEX; --specification of the package package body COMPLEX is . . . end COMPLEX; --body of the package procedure SECOND is . . . --another declarative item procedure THIRD is . . . end THIRD; --a nested procedure end SECOND; begin -- --sequence of statements -- end MAIN_PROGRAM; COMPLEX is visible in the MAIN_PROGRAM from the point at which it is first named. As a result of the scope and visibility rules of Ada, the FIRST procedure cannot see the COMPLEX package; however, the SECOND and THIRD procedures, along with the MAIN_PROGRAM body, can use the resources of the COMPLEX package. Another, and usually preferred, method of establishing visibility is through the with clause. Thus, the package COMPLEX may be separately compiled and may be used by other program units; as in the following example: with COMPLEX; procedure MAIN_PROGRAM is . . . MAIN_PROGRAM is said to import the unit COMPLEX. COMPLEX is then visible throughout the MAIN_PROGRAM, and the type NUMBER and the three functions are available. The benefit of this approach is that it supports the principles of modularity and localization and so helps limit the scope of any changes that may be made during software maintenance. Another more subtle benefit is that it encourages the transportability of software units. Eventually, you'll be able to go to your local software package store, specify the interface you need, and then receive these packages for use with your program units. The structure of Ada packages clearly facilitates the develop- ment of an industry of transportable software modules. Once a package is visible, parts of its specification may be identified with selected component notation (the visible parts only). For example with COMPLEX; procedure SOME_PROGRAM is NUMBER_1,NUMBER_2 : COMPLEX.NUMBER; begin -- --sequence of statements -- end SOME_PROGRAM; Notice how our choice of names made the declaration of NUMBER_l and NUMBER_2 quite readable. This notation may be used with other names, such as: NUMBER_1.IMAGINARY_PART :=37.961; NUMBER_1 := COMPLEX."+"(NUMBER_1,NUMBER_2); The last example looks a bit awkward, since the addition operator is not directly visible and must be selected. Normally, we would like to write our equations using infix rather than prefix notation. Alternatively, we may gain direct visibility through the use clause. Application of this clause permits components to be referenced without requiring the package name as a prefix (as long as the component name is not ambiguous). For example: with COMPLEX; procedure ANOTHER_PROGRAM is use COMPLEX; NUMBER_3, NUMBER_4 : NUMBER; begin NUMBER_3 : = NUMBER_3 + NUMBER_4; end ANOTHER_PROGRAM; In this example, the addition operator refers to the operation exported from the package COMPLEX, not the predefined operator for numeric types. The addition operator is directly visible, and so we may use infix notation and drop the quotation marks. We say that the addition operator is overloaded, and, as long as the compiler can determine the operator to which we are referring (by examining the actual parameters of the call, for example), we may freely use any such overloaded entity. Note another style issue: Ada permits declaration of several objects at the same time (NUMBER_ 3, NUMBER_$) in what is called an identifier list. In most cases, it is recommended that a programmer use only one object name per declaration in order to improve readability. The use clause is sometimes convenient to apply since it permits shorter names, but it can cause a loss of clarity and could introduce a name clash (the condition in which there are two or more entities at the same level with the same name). The use clause does not prohibit prefixing the package name, however; so NUMBER_3 and NUMBER_4 could still be declared as COMPLEX.NUMBER to improve readability. Generally, it's still good practice to avoid the use clause in order to minimize the number of names directly visible at one time.