Variadiic functions

I've been trying to think of an alternative way of formulating the rules for variadic functions in a way that is less complex and confusing, but retains all the functionality. Here's my attempt:

<proposal>

A `function specification` (for example, a function declaration in XQuery, an xsl:function declaration in XSLT, or a specification entry in the F&O spec) declares a `function family`. A function family is a set of functions that share the same name, and that has a range of permitted arities: specifically, a minimum arity which is a finite non-negative integer, and a maximum arity which is an integer in the range 0 to infinity. The static context contains a set of function families; if two function families in the static context have the same name, then their arity ranges must not overlap.

A function specification contains a list of zero or more parameter declarations. Each parameter declaration has a property called its plurality, which takes one of the values `single`, `multiple`, `optional`, or `mapped`. The list of parameter declarations in a function specification must comprise zero or more single parameters, followed by zero or one multiple parameters, followed by zero or more optional parameters, followed by zero or one mapped parameters.

The minumum arity of a function specification is the number of parameter declarations with plurality = single.

The maximum arity of a function specification is infinity if there is a parameter declaration whose plurality is `multiple` or `mapped`; in other cases the maximum arity is the number of parameter declarations.

A static function call is bound to a function family in the static context by considering the function name and the number of supplied arguments. The function call supplies a number of positional arguments followed by a number of keyword arguments. The keywords are NCNames and must be distinct. The function name must match the name of a function family in the static context, and the total number of supplied arguments in the static function call (counting both positional arguments and keyword arguments) must fall within the arity range of one of those function families.

The arguments supplied in a static function call are matched to the declared parameters of the function family as follows:

* Starting from the first positional argument, arguments are matched in order to the parameter declarations with plurality=single.

* If there is a parameter declaration with plurality=multiple, then any remaining positional arguments are aggregated as a sequence and matched to that parameter declaration.

* If there is no parameter declaration with plurality=multiple, then any remaining positional arguments are matched individually, in order, to the remaining parameter declarations, regardless of their plurality

* Starting from the first keyword argument, arguments are matched by name to the first parameter declaration (of any plurality) that has not already been matched. In this matching process, the supplied keyword (an NCName) is considered to represent a no-namespace QName value whose local part is the supplied keyword.

* If there are keyword arguments that do not match the name of any parameter declaration, and if there is a parameter declaration with plurality=mapped, and if the parameter declaration with plurality=mapped has not been matched by one of the forgoing rules, then these keyword arguments are aggregated to form a map, and the resulting map is matched to the mapped parameter declaration. In this process the keywords are treated as xs:NCName values (not as no-namespace QNames).

* It is an error if there is a parameter declaration with plurality=single that remains unmatched [?is this possible?]. If a parameter with plurality=optional is unmatched, then it takes its default value from the parameter declaration. If a parameter with plurality=multiple is unmatched, its value is an empty sequence. If a parameter with plurality=mapped is unmatched, its value is an empty map.

A function reference (`name#arity`) is matched to a function family in the static context whose name matches the supplied name and whose arity range includes the supplied arity.

In dynamic function calls, all arguments are supplied positionally. The supplied arguments are matched in turn to the declared parameters of the function, irrespective of their plurality. 

</proposal>

<commentary>

First, the proposal is a little bit less rigorous than I would like, but I think it can be tightened up to remove any gaps and ambiguities.

The main change from the current draft is that instead of categorizing functions (e.g. as sequence-variadic or map-variadic), we classify parameter declarations by their "plurality" (if anyone can think of a better name, you're welcome). This increases flexibility, for example we can now declare fn:total() (replacing fn:sum) to take a plurality=multiple input sequence followed by an optional argument with keyword "zero", and we can write total(1, 5, 6, zero="0"). (We can't do this with fn:sum, because the existing call sum(3, 0) wouldn't work as intended).

I have omiited from this proposal the examples and explanations that are in the current text, but I think all of the examples remain valid, with minor changes to the terminology of the explanations,

I haven't adopted Reece's idea to allow the keywords in a static function call to be something other than an NCName. Allowing a string literal would not be a problem, but I'm uncomfortable about QNames. The problem here is that unprefixed names mean one thing if matching an optional parameter, and something else if matching a mapped parameter. But the problem isn't insuperable.

</commentary>

Michael Kay

Received on Tuesday, 27 September 2022 06:51:18 UTC