- From: Dimitre Novatchev <dnovatchev@gmail.com>
- Date: Sun, 13 Dec 2020 15:11:48 -0800
- To: Reece Dunn <msclrhd@googlemail.com>
- Cc: Michael Kay <mike@saxonica.com>, public-xslt-40@w3.org
- Message-ID: <CAK4KnZf1W8P==mbM1M5AzhSbeTN+ye4Mq0nFjuw9GYNODg=HzA@mail.gmail.com>
On Sun, Dec 13, 2020 at 2:14 PM Reece Dunn <msclrhd@googlemail.com> wrote: > On Sun, 13 Dec 2020 at 21:07, Dimitre Novatchev <dnovatchev@gmail.com> > wrote: > >> >> >>> >>> >>>> *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. > What issue? If the last positional parameter/argument is of type array, then the last two of the list of arguments in the "effective call" will both be arrays and there is no ambiguity in their interpretation: the last but one argument (array) is the last positional argument, and the last argument (array) contains the variadic arguments. > > 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 { > string-join($args) > }; > > Great, then this means there is no reason why map-variadic function calls should exist. All of these can be record-variadic, thus more amenable to static type-checking. > >> >>> >>>> *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 > name. > Yes, so we both agree that in a record-variadic function call a stronger, static type-checking is possible, than a map-variadic method call (which is not a record-variadic method call). Good result! > > >> >>> >>> >>>> 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). > But the major advantage of record-variadic functions is the stricter type-checking of the variadic arguments provided in the function call. > > >> >>> 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". > I asked for an example of even one function that can be declared as map-variadic but cannot be declared as record variadic. If we have such an example, then this would justify using map-variadic functions. If not (which is the case at present), then there is no justification to declare a function as map-variadic and not as a record-variadic. > > 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. > > Great! I would agree with whatever name is considered best by our native-speakers of English :) > Kind regards, > Reece > Thanks again, Reece :)
Received on Sunday, 13 December 2020 23:12:14 UTC