Named parameters

Named (or keyword) parameters were adopted as a desirable extension to programming languages in the midst fo the "structured programming" frenzy in the 1970s. They address the shortcomings of the traditional scheme of parameter communication, the so-called positional method, still used in most programming languages.

In some programming languages, the positional approach takes the form of actually using the position in nominal fashion, like so (in bash):

                #!/bin/bash

                function foo {     
                    echo $1
                }

The rationale for this particular communication style, in shell scripting, is that arguments, within funtions, are treated in the same manner as arguments given to the script as a whole.

Despite its popularity, On occasion this approach might be a shortcoming from a software documentation point of view. Consider the function: float line(float x, float m, float b){ return(m*x + b); } For a programmer using the function, it's not immediate the identification of which parameter is which, and the fact that all of the parameters are of the same type makes matters even worse. Even if modern IDEs provide enough information while writing the program, for someone reading it later on, the meaning of a call such as line(3.2, 8.2, 1.0); might not be readily apparent. When the number of parameters increases, this situation might become a sizable hurdle for the readers of the program.

Furthermore, if, at some point during the maintenance process, the function line is modified so that it takes another parameter, without significantly affecting the code for the rest of the parameters, every call to line has to be modified, which can become a drag.

Although this situation might be alleviated with a number of techniques involving programming conventions and their disciplined use (designing libraries with a convened upon meaning of their arguments, or using suitably named temporary variables and then calling the function on them), it's probably a good idea to provide a language extension to enforce this discipline to some extent.

The addition of the named parameters mechanisms has the added value of allowing the introduction of default values to some or all of them. It was actually been argued that the real power of the named parameter approach is realized when defaults are introduced [Hard76]. Defaults allow to provide values to parameters that are not explicitly bound in the call point.

Francez has proposed a further extension to the named-parameter passing style, which specifies what type of communication (by value, by reference, by name) the argument is passed to the function.

Disadvantages

There are some instances where keyword communication is inferior to positional. The most obvious is the call to a procedure that is defined having only a single parameter. The keyword is, of course, unnecessary. Generally, keywording without defaults will require more keystrokes; keywording with defaults will require less. For some of today's machines, the processing of keyword sequences will be somwhat more expensive than the processing of positional sequences. The most disturbing disadvantage may be that an unintantional omission of a parameter on the programmer's part might lead to a program executing incorrectly, but without noticeable errors, since the default value for the parameter would be used.

Case Studies

C family

In keeping with the spirit of syntax compactness, C, C++ and Java functions only have positional parameters. C++ allows the specification of default values to some or all of the arguments of a function.

The Pascal langauges

Ada

An Ada function call can look like this (using the above example): line(x => 3.2, m => 8.2, b => 1.0); where the identifier to the left of the '=>' is the name of the parameter, an must match one of the formal parameter names given in the definition of the function (and the value associated with said identifier must be of the same type in the definition). (In addition, parameter lists can have default values, which further enhances both readability and usage). The actual parameter value is then used as the value of the formal parameter with the same name, regardless of its position in the actual argument list in the function application.

In Ada, a combination of positional and keyword parameters can be supplied, as long as the latter appear later than the former (in the absence of the default-value facility, a value for all of the formal parameters must be provided)

Euclid

Statistical Languages (S, S+ and R)

Implementation in CForAll

Issues

Actual parameters can be sorted so that the positional parameters appear before the keyword parameters, and the keyword parameters are in lexicographic order. (Do this as well in the function definition). This is legitimate because the order of evaluation of arguments isn't defined (I hope... I have to look into that). One possible way to implement the processing of these types of parameters in the callee routine is presented at this link. It uses a function called Clause(). Clause() with one parameter simply is a boolean for it's existence. With two parameters it returns the sub-parameter(s) if the given clause. For example, for an SQL-like statement, Clause("Where") would return True if there is a Where clause, and Clause("Where",1) would return something like "status = 'M'". Note that there are other variations that allow for an unknown quantity of parameters, such as field lists. Of course, field names could be put into one big string instead. There are many ways to skin a cat. The final choice usually depends on the orientation of the language. An advantage of this approach is that it can easily be expanded to allow zero or many sub-parameters per clauses if the language builder later decides to add these features. * Pro - Good compromise between positional and named. Can handle both types of parameters. Can also handle non-parametered clauses and potentially multiple sub-parameters per clause. * Con - May be a bit difficult or slow to parse. Not very common outside of database languages, so it may cause some confusion. Option 4 - Mixed Named Parameters This is very similar to mixed clauses. Example: rr(1, 2, "m") rr(1, y=2) rr(m, y=n) rr(1) rr(x=1, y=7, z="hey") rr(y=7, z="hey", x=1) // The subroutine definition sub rr(x=0, y=0, z="a") ... endsub This example shows the subroutine definition with defaults assigned. (Defaults are addressed in the Parameter Receiving section.) * Pro - Good compromise between positional and named. Can handle both types of parameters. Easy to specify defaults. * Con - Allows exactly one parameter per parameter name. (Contrast this with clauses, which can potentially have zero or many sub-parameters per clause.) Favorite We chose either of the mixed parameter types for their versatility, although we lean toward the mixed clause approach for its expandability. 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);

References

[Har 76]
Hardgrave, W.T., Positional versus keyword parameter communication in programming language, ACM, SIGPLAN Notices 11,5 (May 1976), pp. 52-58.
[Fr 77]
Francez, N., Another advantage of Keyword Notation for Parameter Communication with Subprograms, Comm. ACM 20,8 (Aug. 1977), pp. 604-605.