Revised and reviewed April 2020
Named parameters, also called "keyword arguments," may be used in place of, or in conjunction with, the traditional ordered parameters for all function and procedure calls. (For related information as it applies to subroutines, see Named Parameters in Subroutines.) Although this feature is really just a syntactic nicety rather than a fundamental capability, it nevertheless offers a variety of benefits:
• | makes code more self-documenting |
• | reduces errors relating to omitting or mis-ordering parameters |
• | allows functions to specify default parameter values besides 0 or "" |
• | allows functions to specify mandatory parameters |
• | allows caller to specify just the parameters of interest, omitting the defaults |
• | allows the compiler to detect badly formed function calls |
The ASB implementation of named parameters is similar to that found in other modern languages such as Python, in which the parameter names are extracted from the existing function and procedure definitions without requiring any syntactic changes to existing definitions. In addition, function and procedure definitions may specify default values for one or more parameters. Parameters with specified defaults are considered optional parameters, whereas those without defaults are considered mandatory.
However, there is one important exception to this rule, required for backward compatibility. Since ASB has traditionally treated ALL function, procedure and subroutine parameters as optional, it will continue to do so unless a function/procedure definition is modified to specify at least one default value.
This will become clear with a few examples, but first let's note the following points:
• | Named parameters are resolved entirely by the compiler. Thus programs using named parameters are backwards compatible back to the 5.0 runtime, and the runtime system has no way of detecting if a particular call used named or traditional ordered parameters. |
• | The semantics regarding optional and mandatory parameters are entirely determined by the function/procedure definitions, and not affected by whether the routine is called with named or ordered parameters. |
• | Since the compiler needs to see the function/procedure definition before any calls which use named parameters, it makes a new preliminary pass of the entire source to build a table of the definitions. Although this pre-pass is very fast, to avoid inflicting any performance penalty or risk of side effects on programmers not using named parameters, the preliminary pass and thus the named parameter feature requires a new switch: /P. This in turn requires an update to COMPIL.LIT 1.1(126) |
• | In order to create common utility functions and procedures that might need to be compiled by older versions of the compiler, the /P switch will define a symbol ABC_NAMED_PARAMS=1 which you can use in conditional compilation statements to specify alternate versions of your function/procedure definitions. See example below. |
• | Ambiguity/Compatibility Warning: Expressions of the form A=B have always been legal where expressions are legal, and thus Fn'X(A=B) is legal with any version of the compiler, provided that A and B are mapped variables or that (the horror!) you are compiling without the /M switch. The expression evaluates to true (-1) if A equals B, and or false (0) otherwise. Use of the /P switch disables that dubious feature within subroutine and function/procedure calls, and instead interprets the expression as a request to set the named parameter A to the value B. However, there is possibility that calls with named parameters might be compiled under an older compiler or without the /P switch, resulting in a syntactically correct but semantically confused RUN file. To avoid this possibility, in programs that use named parameters, you should include a conditional compilation test to detect an attempt at compilation without /P and to report it as an error, e.g.: |
++ifndef ABC_NAMED_PARAMS
++error This program requires compiler 653+ (and /P)
++endif
Now for the examples. Consider the following function which takes the names of players at the first, second and third base positions and displays them as a sentence, as in the famous Abbott & Costello routine, i.e. "Who's on first, What's on second, and I Don't Know's on third".
function fn'lineup$(first$ as s0, second$ as s0, third$ as s0) as s0
Traditionally, any of the following would be legal invocations of the function, at least from the compiler's perspective:
? fn'lineup$()
? fn'lineup$("Who")
? fn'lineup$("Manny","Moe")
? fn'lineup$(spam$,spam$,spam$,spam$)
The first three examples specify fewer than the three expected parameters, but since there is no enforcement of minimum parameter counts, the compiler is happy. The routine itself can test .ARGCNT to see how many were actually passed. The fourth example specifies an extra parameter, which the compiler is happy to pass, and the routine may simply ignore, although it also has the ability to retrieve such additional parameters using explicit XGETARG statements.
Using named parameters, any of the following are also legal:
? fn'lineup$(first$="Who", second$="What", third$="I don't know")
? fn'lineup$(second$="Jack", first$="Manny")
? fn'lineup$("Eenie", "Meenie", third$="Mo")
The first simply adds the names of the parameters, making the code a bit more self-documenting. The second illustrates that you can rearrange the parameters since the compiler can match the names up to their proper positions, and you can also omit trailing parameters, in this case third$, provided it doesn't create a gap. The third example illustrates that you can combine ordered and named parameters, as long as the ordered ones ("Eenie" and "Meenie" in this case) come first.
These, however, are illegal:
? fn'lineup$(first$="Who", shortstop$="Curly")
! no such parameter: shortstop$
? fn'lineup$(first$="Manny", "Mo", "Jack")
! ordered parameters cannot follow named parameters
? fn'lineup$(first$="Tiny", second$="Fats", first$="Tiny")
! parameter first$ specified twice
? fn'lineup$(second$="What", third$="Nobody")
! missing mandatory first$ parameter
The last example above deserves clarification. Although traditionally all parameters were optional, it was never possible with ordered parameters to omit one, other than at the trailing end of the parameter list. Consequently, to preserve runtime compatibility, we don't want to allow that to happen with named parameters either, at least by default.
However, by adding default values for one or more parameters in the function/procedure definition, we can effectively instruct the compiler regarding which parameters are mandatory and thereby have it do more of the work of error checking and standardizing function and procedure calls. To accommodate default values, a default value may be specified for any parameter in the function declaration, using the following syntax:
<paramname>{=defvalue} as <type>{:modifier}
For example:
function fn'lineup$(first$="Who" as s0, second$ as s0, third$="I don't know" as s0) as s0
Or, if we want to make it clear that these are all inputonly parameters, we can add the :inputonly modifier:
function fn'lineup$(first$="Who" as s0:inputonly, second$ as s0:inputonly, third$="I don't know" as s0:inputonly) as s0
With the updated function definition, we can reevaluate how the compiler would treat the example calls:
? fn'lineup$()
! illegal (missing mandatory second$ parameter)
? fn'lineup$("Who")
! illegal (" " ")
? fn'lineup$("Manny","Moe")
! ok (missing third$ defaults to "I don't know")
? fn'lineup$(spam$,spam$,spam$,spam$)
! ok
? fn'lineup$(first$="Who", second$="What", third$="I don't know")
! ok
? fn'lineup$(second$="Jack")
! ok (first$ defaults to "Who" and third$ to "I don't know")
? fn'lineup$("Eenie", "Meenie", third$="Mo")
! ok
Finally, if we want to make our fn'lineup$() function available in a library where it might be compiled with an older version of the compiler, we can use conditional compilation to specify two versions of the function definition—one traditional (ordered parameters only) and one modern (with named parameters and default values):
++ifdef ABC_NAMED_PARAMS ! if /P (named parameter supported)
function fn'lineup$(first$="Who" as s0, second$ as s0, third$="I don't know" as s0) as s0
++else ! traditional version
function fn'lineup$(first$ as s0, second$ as s0, third$ as s0) as s0
++endif
fn'lineup$ = first$ + "'s on first, " + second$ + ",s on second, and " + third$ + ",s on third."
endfunction
History
2014 January, A-Shell 6.1.1373: Named parameters implemented in A-Shell.