Variadic functions in XSLT

Thoughts on syntax for defining variadic functions in XSLT.

First, we allow parameters of functions to be made optional by adding a select="expression" attribute to define a default value.

What should the rules for the expression be? I think initially, I'll propose that it must be a static expression (as defined for use-when). This minimises the problems of context-dependency, it means that all callers get the same default, and it stops users trying to build a lot of clever logic into the defaults. It also reuses existing rules rather than requiring a new lot.

Obviously a default cannot be defined for a parameter unless defaults are also defined for subsequent parameters.

We need the ability to mark either the function, or the last parameter, with the equivalent of variadic="map" or variadic="sequence", and I haven't yet found a concrete syntax for this marker that I'm especially happy with.

There needs to be a rule that you can't have two functions with the same name and overlapping arity. The complication is how this interacts with the rules for import precedence. I propose that function A overrides another function B with lower import precedence only if they have the same name arity range, and variadicity (!); if they overlap or clash, it's a static error.

The rule for overriding a function in another package is that the name, arity range, and variadicity need to be exactly the same. What about the parameter names? Now that parameter names can appear in calls, a function only substitutes for another function if the parameter names match. However, requiring them to match may cause working XSLT 3.0 code to become invalid. On the other hand, if they don't match, then what does a call with keyword arguments to a function that has been overridden actually mean? One solution that comes to mind is that the parameter names in the overriding function are ignored, and any function call that uses keyword arguments must use the original names. Another possibility is that keywords are only usable in a function that is marked as keyword-callable, and if the function is marked as keyword-callable, then any overrides must use the same parameter names.

xsl:expose and xsl:accept refer to functions by name and arity; internally the whole machinery of component binding across packages uses the combination of function name and arity to refer to a function. There's a danger here that these already complex rules are going to become completely impenetrable. We need to take stock here and remember what we're trying to achieve, and ensure that we're not building an edifice that's completely out of proportion to the requirement we are trying to meet.

I think we can keep things sane with rules along the following lines:

(a) every xsl:function declaration defines a function that is uniquely identified (within a package) by a name and arity. Call this the "declared arity" of the function. The declared arity is the same as the number of xsl:param declarations. Rules for function visibility, xsl:expose, xsl:accept etc are expressed in terms of the declared arity. This basically assures that most of the 3.0 rules carry over unchanged.

(b) a function call may have a variable number of arguments (positional and keyword): for every declared function, there is a "caller arity" which is a range with a minimum and maximum. It's not allowed to have two functions with the same name and overlapping caller arity ranges in the static context.

A couple of other points:

* names of parameters defined in xsl:param can be QNames, but keywords in function calls must be NCNames. I propose that keywords used in function calls can only match a declared parameter if the parameter name is in no namespace. Alternatively, if we require functions to be declared as "keyword-callable", then a keyword-callable function must use parameter names that are in no namespace.

* if we want to allow XSLT-defined functions to emulate built-in functions in the way that the function can depend on the static or dynamic context of the caller, then I think we could meet 95% of the requirement with an attribute on xsl:param taking the form

usage="context-item" -- the parameter is optional and defaults to the context item
usage="collation" -- the parameter expects a collation URI which defaults to the default collation
usage="relative-uri" -- the argument value is treated as a relative URI which is resolved against the static base URI of the caller
usage="QName" -- the parameter expects a lexical QName which is resolved in the caller's static namespace context.

That's terribly ad-hoc, but it's the simplest I can come up with. 

Michael Kay
Saxonica

Received on Tuesday, 15 December 2020 23:56:44 UTC