Re: New XPath draft

On Sun, 13 Dec 2020 at 21:07, Dimitre Novatchev <>

>>> *Quote*:
>>> "%variadic("sequence") indicates that the function is
>>> sequence-variadic. A sequence-variadic function declares one or more parameters,
>>> of which the last typically has an occurrence indicator of * or + to
>>> indicate that a sequence may be supplied.
>>>  *Questions*:
>>> 1. Why sequence-variadic and not array-variadic?
>>> If an array is used, then it can hold for example:
>>>     [1, (), 2, 3]
>>> and all 4 elements of the array will be accessible from the function
>>> call,  not just 3 as in the case when a sequence is passed:
>>>     (1, (), 2, 3)
>>> Thus having array-variadic functions (function calls) is more precise
>>> and expressive, as shown in this example.
>> In the "Variadic functions and dynamic function calls" thread, Michael
>> Kay explained that this is due to `[]` being ambiguous -- is it a single
>> argument to the variadic version and thus passed as `[ [] ]`, or is it the
>> value of the parameter itself? In that thread, I've proposed a
>> fn:function-arguments function as a way to get the array version of the
>> parameters if required:
>>     fn:function-arguments($from as xs:integer := 1, $to as xs:integer? :=
>> ()) as array(*)
>>     (: This function is ·deterministic·, ·context-dependent·, and
>> ·focus-independent·. :)
> Actually, there isn't any ambiguity:
> [ [] ]     is an array with a single item in it, which is the empty array
> (arrays in this respect are different from sequences and do not
> remove/flatten their empty items),
> while [] is the empty array (containing 0 items).

The ambiguity happens at the point at which the function is called. If the
last argument is an array, then passing a single array value could be
interpreted as passing that array to the function (`[]`) or applying the
array-valiadic logic and constructing an array of that parameter and then
passing that (`[ [] ]`) to the array parameter. With sequences (as noted by
Michael Kay), they don't have this issue, as `T` and `(T)` are equivalent.

Note: There is an equivalent potential issue with map-variadic functions.
Given a function `f($options as map(*))`, how will the function call
`f($options := map {})` be interpreted? -- Is that passing an empty map to
the map parameter (via the rule for naming parameters), or constructing a
new `map { "options": map {} }` object (using the map-variadic rules)?

>> 2. If the only reason is that fn:concat()  cannot be expressed/called as
>>> an array-variadic function, can we have both: array-variadic and
>>> sequence-variadic (the latter just for fn:concat() ) defined?
>> It can be expressed as either, however the value flattening for fn:concat
>> is more intuitive when defined as a sequence-variadic function. See the
>> explanation above.
> As per the conclusion above,  that actually there is no ambiguity, a call
> to fn:concat() cannot be expressed as an array-variadic call. I suspect
> that this is the only standard function with this problem.
> Therefore the raised question remains valid and needs an answer.

If an fn:concat function is array-variadic (provided array-variadic
functions are supported), it can be implemented in terms of the `for member
...` expression, or another way of enumerating over the parameters. It is
more complicated to get the same sequence-style flattening logic using
arrays, but not impossible. That is, you could use something like:

    declare (: %variadic("array") :) function fn:concat($args as
array(xs:anyAtomicType?)) as xs:string {
        string-join(for member $arg in $args return string-join($arg))

and that would have the same semantics as a version that is
sequence-variadic, which could be implemented as:

    declare (: %variadic("sequence") :) function fn:concat($args as
xs:anyAtomicType*) as xs:string {

>>> *Quote*:
>>> "%variadic("map") indicates that the function is map-variadic. A
>>> map-variadic function declares one or more parameters, of which the
>>> last must be a type that accepts a map .
>>> *Questions*:
>>> 1. Why not  “record-variadic”?
>>> Unlike the map type, the new record type allows for static typing (and
>>> if dynamic typing is really necessary, an “extended” record type may be
>>> used). Thus, using “record-variadic” will be an improvement over
>>> “map-variadic”.
>> Note that record types are not specifically a new type, but are a subtype
>> of maps (in that they provide additional constraints on maps). The current
>> draft text permits either map or record types, depending on whether or not
>> you want to restrict the values.
> Following this logic one can say that both: the record type and the map
> type are examples of the function type. Then why not call all these
> "function-variadic"? :)
> We have the chance to use a most precise name, and this is
> "record-variadic", unless someone can show an example of a standard
> function that can be called as "map-variadic" (passing a map) but not as a
> record-variadic (passing a record).
> So, this question still needs a definite answer.

Map and array types are function types in that they can be called like
other functions can, so `$array(2)` and `$map("test")` are valid (in other
words, you can cast a map/array to its super type, but not cast a function
to its sub-type unless it is an instance of that sub-type). Maps and arrays
have additional behaviour/logic that makes them incompatible with each
other, and functions do not hold a collection of values, or a collection of
(key, value) pairs.

A record type defines the keys that are valid for a given map and the types
those keys can have. As such, a record typed variable can be passed to a
map (provided the key and value types are compatible), as a record type is
a valid map. -- Therefore, a record-variadic function is a special case of
map-variadic functions. The only difference between these is the
validation/type checking and that is not dependent on the variadic nature
of the function. That is, map-variadic rules will convert the argument
names into a map (for a map or record type) and the RecordTest rules will
check the validity of that resulting map. An implementation (or editor/IDE)
could provide more helpful messages by placing the error on the argument

>>> 2. Is there any example of an existing standard function that cannot be
>>> expressed/called as a record-variadic function, but can be called as a
>>> map-variadic function?
>> I think it would be useful to define the map-variadic standard functions
>> using RecordTests, as that would permit static-time error checking.
> Exactly, and this is why we should give it the precise name:
> "record-variadic"!

But map/record-variadic functions are the same in how the argument names
are built into a map (just like we don't have a record {...} constructor
for record types).

>> I don't think we should be restricting this to how these are used in
>> standard functions (otherwise you end up with a situation like with
>> annotations where users can add their own annotations, but the language
>> doesn't provide mechanisms to take advantage of them without vendor
>> extensions/functions). For example, someone could create a json-object
>> function that is map-variadic as it can take any parameters. -- Given
>> Michael Kay's comments about someone not being able to find an array
>> function, it may be useful to add an fn:array and fn:map function, in which
>> case that would be an example of a map-variadic function that cannot use a
>> RecordTest.
> Sorry, you lost me here :(

If we only define/allow record-variadic functions because no map-variadic
functions are defined for standard functions, that would prevent users from
defining/using map-variadic functions if needed -- that is what I meant by
"restricting this to how these are used in standard functions".

The json-object (or json:object) example (and the possible standard fn:map
function) would have the signature `fn:map($map as map(*)) as map(*)` but
cannot be defined as record-variadic in a meaningful way, as they can allow
any key/value pairs. Additionally, a record test requires at least one
key/value to be defined and it is not possible to specify that for a map
that can take any values. Another example would be a map-to-xml function.

>>> *General question*:
>>> In the case when a function call can be array-variadic or
>>> record-variadic, the caller may prefer to pass just one array (or a record)
>>> containing the variadic arguments. In this case wouldn't it be good to
>>> allow the ability to specify as a keyword argument this array (or record)
>>> and have a standard name for these (for example "varargs")? I believe this
>>> will improve the readability of the code.
>> My reading of the %variadic annotation is that it is an infered
>> annotation that depends on how the function is declared, and is a way of
>> adding a new property to functions without modifying the data model.
>> Otherwise, the statement "%variadic, which is present on all static
>> functions" would not make sense. -- It may be worth noting how the default
>> value is calculated, like is done with %public/%private. (This also implies
>> that you could turn off variadic behaviour by adding %variadic("no") to a
>> function that would otherwise be variadic.)
>> The intention is to have this be automatic, so a varargs or similar
>> keyword should not be needed. For sequence-variadic functions, a possible
>> syntax of `ItemType...`/`SequenceType...` has been discussed in other
>> threads and the variadic arguments proposal in the xpath-ng project.
> I never said that naming the last argument was "needed". Just that using a
> standard name for it will increase the readability of the code, when the
> function call provides the complete array or record as the single last
> argument.

Ah, ok, I see what you mean now. Thanks for the clarification.

The term "variadic" is common -- "varargs" is a compound of "variadic
arguments". In XPath/XQuery terminology, "variadic" would be the better
version as you are creating a variadic function, and XPath/XQuery limit
talking about arguments to where the function is called.

Kind regards,

Received on Sunday, 13 December 2020 22:14:48 UTC