CREATE(FILE => MY_FILE, NAME => "FINALTEXT.FEB.15");
Where long parameter lists are common and have default values, as in the job control area, this form of named parameter association provides especially high readability. It may be used in conjunction with the default value facility available for an in parameter if no explicit value is provided within the call. For example, a simulation package may declare the procedure ACTIVATE as follows:
procedure ACTIVATE (PROCESS : in PROCESS_NAME; AFTER : in PROCESS_NAME := NO_PROCESS; WAIT : in DURATION := 0.0; PRIOR : in BOOLEAN := FALSE); |
As shown in this declaration, the parameter PROCESS must be provided in all calls (because no default expression is given). On the other hand the parameters AFTER, WAIT and PRIOR may be omitted. Thus the two following calls of ACTIVATE are equivalent:
ACTIVATE(PROCESS => X, AFTER => NO_PROCESS, WAIT => 0.0, PRIOR => FALSE); ACTIVATE(PROCESS => X); |
Clearly in many contexts the order of parameters is either highly conventional (as for coordinate systems) or immaterial (as in MAX(X,Y)). Hence Ada admits both conventions. The classical positional notation may be used whenever the programmer feels that named parameters would add verbosity without any gain in readability.
The two notations may also be combined, with positional parameters appearing first; that is, once naming is used the rest of the call must use naming. This allows the default value mechanism to be used even when positional notation is desirable, as in the following examples from graph plotting and simulation:
MOVE_PEN(X1, Y1, LINE => THICK); MOVE_PEN(X2, Y2, PEN => UP); ACTIVATE(X); ACTIVATE(X, AFTER => Y); ACTIVATE(X, WAIT => 50*SECONDS, PRIOR => TRUE); |
As shown in this last example, the named notation may be used in conjunction with the default parameters to provide a high degree of expressivity and readability. For the activate primitive in Simula, this could only be achieved at the expense of predefined syntax.
Finally the default parameter facility can be used in conjunction with overloading, thereby allowing further possibilities. These are illustrated by the declarations of PUT in the generic package INTEGER_IO:
procedure PUT (FILE : in FILE_TYPE; ITEM : in NUM; WIDTH : in FIELD := DEFAULT_WIDTH; BASE : in NUMBER_BASE := DEFAULT_BASE); procedure PUT (ITEM : in NUM; WIDTH : in FIELD := DEFAULT_WIDTH; BASE : in NUMBER_BASE := DEFAULT_BASE); |
Given the declarations
F : FILE; N : NUM; |
we can issue the following procedure calls for output on the file F:
PUT(F, N, 10, 8); -- width 10, octal base PUT(F, N, WIDTH => 10, BASE => 8); -- more explicitly PUT(F, N); -- default width, decimal base |
We can also issue similar calls for output on the current default output file:
PUT(N, 10, 8); PUT(N, WIDTH => 10, BASE => 8); PUT(N); |
Overloading and default parameters are complementary: In theory, we could achieve the desired flexibility of procedure calls by means of overloading, but this would require a procedure declaration for each possible form of call (eight instead of two in the above example). On the other hand default parameters provide a concise - and thereby convenient - formulation. But - as the above example shows - if we want to omit the first parameter without using named associations, this will have to be achieved by overloading.
The example of the two PUT procedures further illustrates that the default expressions need not be static: DEFAULT_WIDTH and DEFAULT_BASE are variables. Another example of the dynamic computation of default expressions is provided by the following procedure ADMISSION: Admission requires a key, a new one being allocated by default in the absence of an explicit one:
procedure ADMISSION(K : in KEY_NAME := new KEY);