- From: Reece Dunn <msclrhd@googlemail.com>
- Date: Tue, 8 Dec 2020 12:33:10 +0000
- To: Michael Kay <mike@saxonica.com>
- Cc: public-xslt-40@w3.org
- Message-ID: <CAGdtn27X=iJ21U6SZpZ3cYjGPqfVrXq4uHkV41q9MzujuG5ZDQ@mail.gmail.com>
On Tue, 8 Dec 2020 at 08:57, Michael Kay <mike@saxonica.com> wrote: > Here is an attempt to consolidate a proposal for variadic functions that > combines all the ideas and requirements that have been expressed. > > I would worry that the proposal is over-complex, if it weren't that Python > seems to offer all these options and users seem quite happy with it. > > Note: I use the word parameters to refer to things in the function > declaration, and arguments to refer to things in the function call. The > distinction is important. > > TODO: I haven't tried yet to define rules for partial application. > > A. Some general rules about variadic functions. > > If a function is variadic, then the number of arguments in a function call > can differ from the number of parameters in the function declaration. > > If a function is variadic, then its name must be unique within the static > context. > > A variadic function can be identified by a function reference of the form > name#*. fn:function-arity applied to a variadic function returns (); > fn:function-lookup and fn:function-available accept a second argument of () > to identify a variadic function. > > A function call contains zero or more positional arguments followed by > zero or more keyword arguments. Keyword arguments can also be used when > calling non-variadic functions, though the number of arguments in that case > is fixed. > > We identify three kinds of variadic function, which I will call > bounded-variadic, array-variadic, and map-variadic. > > B. Bounded-variadic functions. > > In a bounded-variadic function, one or more of the parameters are declared > as optional, with a default value. > > The number of supplied arguments in a function call must be >= the number > of non-optional parameters, and <= the total number of declared parameters. > (Hence the name, bounded). > > A positional argument is bound to the parameter at the corresponding > position. A keyword argument is bound to the parameter with the > corresponding name. It is an error if two arguments are bound to the same > parameter, or if no argument is bound to a required parameter, or if too > few or too many arguments are supplied. > > TODO: define the rules for evaluation of default values, e.g. can they be > context dependent (if so, what is the context), can the default value of > one argument depend on the supplied values of other arguments, etc. > > Typical example: format-date(). This can now be redefined as a > bounded-variadic function with 5 declared parameters of which the last > three are optional. Calls with two positional arguments and five positional > arguments remain valid; it also becomes possible to supply 3 or 4 > positional arguments, and to supply any of the arguments (including the > required arguments) using keywords. > +1 I like the concept of bounded-variadic functions as the mechanism to support default arguments. It fits in with the rest of the proposal, and works well with the logic for array-variadic functions. For compatibility, format-date#2 and format-date#5 remain available as > references to virtual fixed-arity functions that map directly to the > variadic function. > -1 My concern here is introducing functionality that is not defined as part of the language in mapping the function references with the special casing logic to create virtual-arity functions, so we end up in the same situation as the current variadic arguments for concat, and for context-sensitive functions. It makes it difficult for tool vendors to support (especially if the functions the special casing applies to varies between vendors), and has the logic of only applying to the standard library and not other functions. For example, MarkLogic has a lot of functions that have default arguments, so it would be helpful if they could support this without having to special case those functions as well. The way I've approached this for array-variadic functions is to have the function arity be a (min bound, max bound) range where a function reference or call matches if the argument arity (a scalar value) is within the function parameter arity. For a non-variadic function the min and max bounds are the same. I could easily see using that same mechanism to support bounded-variadic functions. It also means that there is no special case logic to define fixed-arity virtual functions, and would allow vendor-specific and user-defined functions to be rewritten to make use of default arguments while retaining compatibility with any references bound to them. > C. Array-variadic functions. > > In an array-variadic function, the last parameter has an array type. If > the last parameter is at position N, then the supplied arguments at > position N, N+1. N+2, etc, are combined into an array, and the array is > passed as the value of the last parameter. The caller also has the option > of passing an actual array as the value of the argument; this is done by > using a keyword argument. The array has an implicit default of []. > > The number of supplied arguments must be >= the number of required > parameters (where the array argument is not counted as a required > parameter: it effectively defaults to an empty array). There is no upper > bound on the number of arguments. > > Typical example: concat(). All existing calls of concat remain valid, as > do function references such as concat#17. But it now becomes possible to > supply a single argument as an array, by keyword. > +1 with my caveat around not having function references be special cased for variadic functions. > D. Map-variadic functions. > > In a map-variadic function, the last parameter has a map type. All keyword > arguments in the function call are combined into a map, which is supplied > as the value of this last parameter. Values for parameters other than the > last can only be supplied positionally. It is also possible for the > function call to supply the map as a single argument, in which case it must > be supplied as a positional argument. The map has an implicit default of > map{}. > > The number of supplied arguments must be >= the number of required > parameters (where the map argument is not counted as a required parameter: > it effectively defaults to an empty map). There is no upper bound on the > number of arguments. > > Typical example: serialize(). You can supply any keywords you like; the > function will ignore any that it doesn't recognise. > +1 This should be supported for record types as well, in which case the number of arguments would be bounded and you would be able to have validation on the additional arguments. It will also allow tool vendors to support better type checking and auto-complete support for those functions. If you want it to be open-ended, you could use an open-ended/extensible record type. Kind regards, Reece > Michael Kay > Saxonica >
Received on Tuesday, 8 December 2020 12:33:35 UTC