Revised April 2020; added example 4
Parameters in function and procedure definitions have an optional "as <typsiz>{:<mod>)" clause to declare the type, size, and modifiers for the parameter. This also applies to the return type of the Function. If no clause is specified, the default type and size follows the normal rules for BASIC. The fact that you probably aren't 100% certain what those rules are should reinforce the importance of always defining the type and size explicitly. The following examples will help clarify the syntax:
Example 1
FUNCTION Fn'Test(p1 as S10, p2 as f6, p3 as B2, p4 As x256) AS i2
This illustrates the fact that although the <typsiz> clause uses the same fundamental data types as in MAP statements, here there is no comma between the type and size (e.g. "var2 as f6" instead of "map2 var2,f,6"). It also illustrates the case insensitivity of the native type codes (F, S, B, X, I) and the "AS" keyword.
Example 2
Function fn'display'name$(cus as ST_CUS_REC, bold as BOOLEAN) as s MAX_NAME
This shows the possibility of using symbolic types and sizes rather than just the fundamental types and hard-coded sizes. One possibility is to use a defined structure (i.e. "as ST_CUS_REC"). See DEFSTRUCT. Another is to use a symbol defined via a deftype statement (i.e. "bold as BOOLEAN"). Syntactically, it's impossible to tell the difference between a defined structure name and deftype name, but the assumption here is that BOOLEAN was defined via DEFTYPE BOOLEAN = I,2.
A third possibility is to replace just the size with a symbol, e.g. "as s MAX_NAME"). In this case, a single space is required between the native variable type ("s") and the symbol name ("MAX_NAME"). Using a symbol rather than a hard-coded number is useful for avoiding a common bug that may happen when passing string arguments to functions, where you decide to increase the size of the string field elsewhere in the application but forget about the function, resulting in the value being truncated within the function. But the technique has largely been superseded by the DEFTYPE method, which eliminates such discrepancies both in the parameter size and its type.
For string and X parameters, another way to avoid the truncation bug just described is to use Dynamically-Sized Variables within the function.
Example 3
function fn'test(p1 as s20:inputonly, p2 as ST_PHONE:outputonly)
This illustrates the use of the optional :<mod> clause to specify a modifier affecting the way the parameter is to be used. Currently the available modifiers are inputonly and outputonly.
The inputonly modifiier serves primarily as an aid to self-documenting code, but will also cause the compiler to generate an error if you explicitly reference the parameter in an XPUTARG statement using a literal parameter number. There is no run-time aspect of this feature, so you won't be stopped from outputting to the parameter using a variable to specify the parameter number in the XPUTARG statement.
The outputonly modifier also aids in self-documenting your code, but in addition it skips the input parameter binding operation (which, for very large parameters could save a lot of CPU cycles).
In the absence of an inputonly or outputonly modifier, the parameter is consider input/output. Note however, that since parameter passing is, in almost all cases, by value (as opposed to by reference), there is no automatic updating of the variables passed by the caller, unless you use explicit XPUTARG statements.
Example 4
function Fn'Get'Date'Time$(dt=-1 as b4:outputonly, tm=-1 as b4:outputonly) as s40
This illustrates the combination of default (=0) value and outputonly clauses, which can be tricky. Without the default value clause, outputonly parameters would always start out as zero or null, which makes perfect sense. Adding a default value clause introduces two behavioral refinements, one of which is always helpful while the other can be confusing and could go either way.
The first refinement is that the caller now has the ability to ignore and or change the order of the parameters. For example, if you just want the time, you don't need to specify the dt parameter, e.g.:
call Fn'Get'Date'Time$(tm=TIME'NOW)
Without the default value for the dt parameter in the function declaration, the compiler would complain about the above statement due to the missing mandatory dt parameter.
The second refinement is that the default value effectively disables the outputonly modifier, meaning that the above all would initialize the value of the tm parameter within the function to the initial value of the TIME'NOW variable in the caller's context. This seems counter-intuitive but turns out to be useful by providing a way for the function to detect whether the caller specified each argument or not. For that purpose, typically you would set the default value to something more unusual (e.g., -1 in this example). By ignoring the outputonly modifier and receiving the caller's passed in parameter values, the function could detect whether the dt parameter was actually specified by whether it's value is -1. The only reason why you might care is if calculating the return value was computationally costly, motivating you to avoid it if the caller wouldn't be able to receive the updated parameter anyway.
That does beg the question of why even specify outputonly when also specifying a default value. The only point would be for self-documentation purposes: labeling the parameters might help you size up the function when you come back to it years later to enhance it. In such a case, just make sure that you don't assume that the initial parameter value will match the default value—i.e. make sure you explicitly initialize those parameters within the function to avoid the possibility of inadvertently passing back a value that you thought was set to the default, but in fact was set to whatever the value of the incoming parameter was.
History
2009 April, A-Shell 5.1.1145: Feature added to A-Shell