-
Notifications
You must be signed in to change notification settings - Fork 1
Variadic parameters
Note: the syntax described on this page is available from GeoDMS version 20.9.0 and later.
A function normally binds a fixed number of arguments: the call must supply exactly one argument per declared parameter. A variadic function relaxes that rule for its last parameter, so that one name captures a variable-length tail of trailing arguments.
A rest parameter is a trailing parameter, written ...name, that binds one or more trailing arguments of a call.
This is the mechanism behind the classic N-ary folds — concat, add, replace_value and similar — where the same name accepts any number of arguments.
A parameter written ...name is a rest parameter. It must be the last parameter of the function, and there may be at most one. Inside the body the rest name may appear only as the trailing argument of another function call, where it splices the captured argument tail into that call. No list value is ever formed; the rest name is purely capture-and-splice.
Combined with an arity-dispatched set of variants, a rest parameter expresses a fold as structural recursion. The following concat joins any number of strings:
function concat
{
variant none() -> parameter<string> := '';
variant one(attribute<string> a) -> attribute<string> := MakeDefined(a, '');
variant more(attribute<string> a; ...rest) -> attribute<string> := MakeDefined(a, '') + concat(rest);
}
Each call selects a variant by the number of arguments supplied. The more variant peels off the first argument a and passes the remaining tail on to concat(rest); because every step consumes at least one argument and the base variants have a fixed arity, the recursion terminates:
parameter<string> c1 := concat(p);
parameter<string> c2 := concat(p, q);
parameter<string> c3 := concat(p, q, r);
attribute<string> ca (D) := concat(D/sa, '-', D/sb);
Here c1 yields 'P', c2 yields 'PQ', c3 yields 'PQR', and ca joins each row of sa, a literal '-' and sb.
A rest parameter is only valid in the final position. Declaring further parameters after it is a parse error:
function bad(...r; container x) -> container := x; // rejected: '...' must be the last parameter
The number of arguments at a call site is used to choose which definition handles the call. Several variants sharing one function name are distinguished purely by their argument count, and a rest variant matches any count from its declared minimum upwards.
This extends to the built-in operators and functions: a user (or prelude) function may share a registered operator's name, provided it only claims argument counts the operator itself does not accept. The operator then serves as the base case of the fold. For example, the + / add operator handles exactly two arguments, so a same-named add function may claim one argument and three-or-more:
parameter<uint32> a1 := add(7);
parameter<uint32> a3 := add(1, 2, 3);
parameter<uint32> a4 := add(1, 2, 3, 4);
The two-argument case is served by the operator; the three- and four-argument calls fold down through the function until the last step reaches the binary operator. The same pattern gives multi-argument mul, or, and, a two-argument log, and a five-or-more-argument replace.
The overlap is validated at declaration. A function may share an operator's name only where their argument counts do not intersect; declaring the operator's own arity under its own name is a semantic error:
function add(container a; container b) -> container := a; // rejected: 2-arg 'add' is the operator's own arity
Only a rest parameter admits a variable count. A function declared with ordinary parameters must be called with exactly that many arguments — supplying too few (or too many) is an arity error:
function F(
unit<uint32> Rd;
attribute<float64> x (Rd))
-> attribute<float64> (Rd) := 2.0 * x;
attribute<float64> bad (Road) := F(Road); // rejected: F expects 2 arguments
20.9.0
GeoDMS ©Object Vision BV. Source code distributed under GNU GPL-3. Documentation distributed under CC BY-SA 4.0.