@qt4cg statuses
This page displays recent status updates about the QT4CG project.
The are also captured in an RSS feed.
By year: 2026, 2025, 2024, 2023, 2022, 2021, 2020
QT4 CG meeting 181 draft minutes #minutes-09-22
Draft minutes published.
Issue #2789 closed #closed-2789
2787 XQuery changes to merge declare record and declare type
Issue #2909 closed #closed-2909
Binary functions: Base64 URL en-/decoding
Issue #2910 closed #closed-2910
2909 Binary functions: Base64 URL en-/decoding
Issue #1777 closed #closed-1777
Shallow copy in XSLT with maps and arrays
Issue #2912 closed #closed-2912
1777 Revise XSLT shallow copy for JNodes
Issue #2073 closed #closed-2073
JNodes and Sequences
Issue #2878 closed #closed-2878
2073 JNodes and Sequences
Issue #2861 closed #closed-2861
Edge cases with CSV serialization
Issue #2862 closed #closed-2862
2861 Clarify CSV serialization edge cases
Issue #2785 closed #closed-2785
Make XSLT tunnel parameters available dynamically as a map
Issue #2925 created #created-2925
`bin:to-base64url`, `bin:from-base64url`: other names?
https://github.com/qt4cg/qtspecs/pull/2910 was accepted today. We could discuss better names for the functions.
Issue #2924 created #created-2924
NodeTest for JNodes
JNode node test discussion
We could also redefine the node test
*, when applied to JNodes, so it only selects a JNode whose jvalue is a singleton map or array; to select other nodes, use the node test jnode(). That's a good analogy to the fact that with XNodes,*only selects elements.
Originally posted by @michaelhkay in #2922
Does that mean that foo selects only jnode(foo, (array(*)|map(*)|sequence(*)) ) (or sort of)?
In that case, we would need something like atom(bar) or @bar to select jnode(bar, *)[not(child::jnode(*))][not(self:(array(*)|map(*)) )].
That probably makes sense.
There is a differences here compared to XML. In XML, typically, most child nodes that have a name are elements, whereas most child nodes that are not elements are nameless. However, in map/array structures, most atomic nodes have a name, as well as not atomic nodes.
QT4 CG meeting 181 draft agenda #agenda-09-22
Draft agenda published.
Issue #2923 created #created-2923
Idempotent Coercion
We already discovered that coercing a function F to a required type T may be necessary even if F is already an instance of T, because the coerced function performs stronger checks on the types of the arguments supplied (see issue #1020).
We now have a test case:
<test-case name="recordTypeDecl-050" covers-40="PR1874">
<description>Record types: field order, subtyping and coercion</description>
<created by="Christian Gruen" on="2026-09-16"/>
<test><![CDATA[
declare record local:AB(a as xs:integer, b as xs:integer);
declare record local:BA(b as xs:integer, a as xs:integer);
declare function local:f($r as local:BA) { map:keys($r) };
local:f(local:AB(1, 2))
]]></test>
<result>
<assert-deep-eq>"b", "a"</assert-deep-eq>
</result>
</test-case>
which demonstrates that coercing a record R to type T may be necessary even if R is already an instance of T, because it changes the order of entries in the record (the instance of check does not require fields to be in the right order).
This feels very counter-intuitive to me. On an ordinary English-language usage, "coercion" means modifying something to meet a given requirement, and shouldn't be necessary if the requirement is already met. If the order of fields is important enough to justify re-ordering, then it should also be important enough for instance of to fail.
I would like to see if we can adopt a general principle that if a value V is an instance of a type T, then coercion of V to T should be idempotent. This principle is important to allow type checking to be performed statically.
(There's an argument that if a function expects a record of type R, then the implementation of the function body should be able to make assumptions about the order of fields, in order to enable more efficient access. But on that line of reasoning, "instance of" should be a stronger test, so that the same logic can be used (say) in a branch of a typeswitch).
This may involve looking again at coercion of function items, which I previously thought was the only exception to this rule.
Another way of solving the problem for function calls would be to change the "instance of" rules. For example, given the example from the current spec,
let $f as function(xs:integer) as item()* := function($x) { $x + 1 }
return $f(12.3)
we would need to change the definition of "instance of" so that function($x) { $x + 1 } is no longer an instance of function(xs:integer) as item()* (instead, it is merely coercible to that type). As far as I can tell, that simply means removing the "contravariance" provision on argument types (§3.3.2.6 rule 2(f)). It seems a reasonable change, because the supplied function doesn't do everything that the expected type requires - it doesn't reject non-integer arguments.
An alternative solution would be to revert to the 3.1 spec here: no coercion is performed in this case and the above query succeeds, returning 13.3.
Issue #2922 created #created-2922
Representation of sequence of items as a sequence of JNodes
Some ideas for discussion.
In the approach where sequences are represented as adjacent JNodes with the same jkey, a single JNode cannot serve to group an arbitrary sequence of items. Therefore,
xsl:array-membershould likely return a single-member array (and the sequence of them are concatenated witharray:join), much likexsl:map-entryreturns a single-entry map (and a sequence of them are concatenated withmap:merge).
Originally posted by @ruv in #2073
This approach was roughly described in my comments on 2025-12-04 and 2026-09-01.
Example:
<jnode kind='map' key='()' value='{ "d": (7, [ 8, (9,10) ], 11), "e": (12,13), "f":14 }'>
<jnode kind='leaf' key='d' value='7'/>
<jnode kind='array' key='d' value='[ 8, (9,10) ]'>
<jnode kind='leaf' key='1' value='8'/>
<jnode kind='leaf' key='2' value='9'/>
<jnode kind='leaf' key='2' value='10'/>
</jnode>
<jnode kind='leaf' key='d' value='11'/>
<jnode kind='leaf' key='e' value='12'/>
<jnode kind='leaf' key='e' value='13'/>
<jnode kind='leaf' key='f' value='14'/>
</jnode>
Some problems of this approach are:
- when copy a JNode (by
xsl:copy-oforxsl:copy), jkey should be erased if the parent type isjnode(*,array(.*)), but we still have to separate new jnodes per array members. - it is unclear how to handle jkey conflicts when constructing
jnode(*,map(.*)). - selecting the next JNode withing the same array-member (or map-entry) without knowing the jkey is too verbose.
To address these problems, a peer binary relation (a partial order) can be introduced in addition to the sibling relation. The peer relation should be preserved when JNodes are copied. Only JNodes that wrap items from the same sequence (e.g., from the same array-member) stand in the peer relationship to each other. Perhaps, it may be advisable to disjoin the peer and the sibling relations (that is, two JNodes cannot be in both peer and sibling relations at the same time).
The corresponding axes: preceding-peer::, following-peer::
/e[1] => jvalue() returns 12
/e[1]/following-peer::*[1] => jvalue() returns 13
/e[1]/following-sibling::*[1] => jvalue() returns 14
/d[last()] => jvalue() returns 11
/d[last()]/following-sibling::* => count() returns 3
/*[not(preceding-peer::*)] returns /( d[1] | e[1] | f[1] )
/*[not(following-peer::*)]/jvalue() returns (11, 13, 14)
To construct an array from a sequence $s of JNodes:
<xsl:array>
<xsl:for-each-group select="$s" group-start-with="*[ not(preceding-peer::*) ]">
<xsl:array-member select="current-group()/jvalue()"/>
</xsl:for-each>
</xsl:array>
Probably, it should be an error if peer JNodes in $s are not transitively adjacent.
If two nodes are not peers and have the same jkey, this is only case requiring duplicate keys handling.
Update: to make the "peer" relation useful for XNodes too, the "sibling" relation should not change (i.e., it should not be disjoint from "peer").
Issue #2921 created #created-2921
Lax record coercions
We’ve decided that…
let $coord as record(x, y) := { 'x': 1, 'y': 2, 'z': 3 }
return $coord
…raises an error. We could redesignate the deprecated extensibility syntax and use…
(: yields { 'x': 1, 'y': 2 } :)
let $coord as record(x, y, *) := { 'x': 1, 'y': 2, 'z': 3 }
return $coord
…to create a record with two entries that discards the remaining ones.
Pull request #2920 created #created-2920
2813 clarifications for xsl:result-document
The primary purpose of this PR is to clarify the situation when an xsl:result-document instruction has an empty or absent href attribute, and thus writes to the principal output destination.
However, it also takes the opportunity to add many more clarifications to the serialization part of the XSLT spec, to improve the order of presentation, and to remove some redundancy
Fix #2813
Issue #2919 created #created-2919
XQFO: feature requests from users
Some requests of “early adopters” (not sure whether that’s still the right term ;·), for functions that would be simple to add or modify:
1. fn:trim
Many functions come with a function for removing leading and trailing whitespace. It would be an easy-to-read shortcut for replace($line, '^\s+|\s+$'), i.e. for users who are not into regex.
2. fn:index-of-substring($string, $substring, $start?)
An alternatives for strings would simplify life (fn:index-of compares items, not strings).
3. fn:random-number-generator()?take(n)
Retrieving n random numbers is hard to achieve for non-experts. A shortcut would be desirable, for code like:
fold-left(1 to $n, random-number-generator(), fn { head(.) ! (?next(), ?number), tail(.) })
=> tail()
Pull request #2918 created #created-2918
2600 Options on fn:collection()
Defines an options parameter for the fn:collection and fn:uri-collection functions.
Fix #2600 Fix #285
Issue #2917 created #created-2917
Implementation-defined features
Some of the specifications have an appendix listing implementation-defined features. In some cases at least this is based on markup within the document text. In all cases, it has not been systematically reviewed or updated for changes in 4.0. We should either drop these appendixes or ensure they are accurate.
Pull request #2916 created #created-2916
2915 `fn:current`: inline functions and `fn:function-lookup`
Closes #2915
Issue #2915 created #created-2915
`fn:current`: inline functions and `fn:function-lookup`
The current spec says:
While the body of a function declaration or the initializing expression of a variable declaration is evaluated, it reverts to the context value of the query prolog.
It is unclear what current() returns in an inline function called from a default value. I think the current value should revert in every function body, just as the focus is absent in every function body.
The spec also lists the constructs that pass the caller’s context value to a default:
a static function call, a named function reference, or a partial function application
We should add fn:function-lookup (it can also create a function item with an omitted argument).
Issue #1459 closed #closed-1459
Function properties and arities
Issue #2886 closed #closed-2886
1459 Function properties and arities
Issue #2901 closed #closed-2901
element-to-map: errors in the conversion plan
Issue #2903 closed #closed-2903
2901 element-to-map: errors in the conversion plan
Issue #2906 closed #closed-2906
Alternative for `+:=`: `put`?
Issue #2907 closed #closed-2907
2906 Alternative for +:=: put
Issue #2883 closed #closed-2883
parse-xml: strip-space=conditional
See 6055 more statuses in yearly archives.