This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 28319 - [FO30] (and [FO31]) Text on least common type and conversion in fn:min and fn:max ambiguous
Summary: [FO30] (and [FO31]) Text on least common type and conversion in fn:min and fn...
Status: RESOLVED WORKSFORME
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: Functions and Operators 3.0 (show other bugs)
Version: Recommendation
Hardware: PC Windows NT
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-03-23 18:10 UTC by Abel Braaksma
Modified: 2016-01-26 16:27 UTC (History)
1 user (show)

See Also:


Attachments

Description Abel Braaksma 2015-03-23 18:10:58 UTC
Under 14.5.3 fn:max we say:

<quote>
Numeric and xs:anyURI values are converted to the least common type reachable by a combination of type promotion and subtype substitution. See Section B.1 Type Promotion XP30 and Section B.2 Operator Mapping XP30.
</quote>

It is unclear to me what "least common type" refers to, which is not defined in either XP30 or FO30. Section B.1 does not use this term. It talks about promotion. One way to read "least common" is to say that it must be deeper in the type hierarchy, but we do not have a strict type hierarchy (see also LCT discussion for XSLT in bug 24569), xs:double does not 'inherit' from xs:float.

The term "least common" is easily misunderstood, should it mean something similar to the lowest common ancestor concept (http://en.wikipedia.org/wiki/Lowest_common_ancestor)? But we do not have a clear graph relationship between the numeric types.

Another way to read "least common" is to write it as "most specific", which means for me that xs:byte is more specific than xs:short, which is clearly not its intention.

It is also ambiguous to me what "is converted" means here. It suggests that the type is converted and the original type is lost, as with type promotion (section B.1) but section B.2 suggests the opposite, with substitution, where the original type is retained. Tests with Saxon give ambiguous results (or I miss something).

I.e.:
max((xs:byte(10), xs:short(9)))
>> is result of type xs:byte, xs:short, or xs:integer (the LCT)? Assumed: byte

max((xs:integer(12), xs:decimal(10)))
>> Assumed: result of type xs:integer expected (type substitution), but unclear (and Saxon returns xs:decimal)

max((xs:float(1), xs:decimal(2)))
>> result should be xs:float, but the LCT relation is vague

In layman's words, I think the common rule for numeric and xs:anyAtomicType is:
- substitute the type for any of xs:integer, xs:decimal, xs:float, xs:double
- substitute xs:integer for xs:decimal
- the first that applies:
  - if seq contains xs:double or xs:anyAtomicType, promote all to xs:double
  - if seq contains xs:float, promote all to xs:float
- compare using op:numeric-greater-than.
- return promoted (changed) or substituted (unchanged) type and value

---------------

Also note that in FO30, Section 4.3 Comparison operators on numeric values, the text reads:

    one argument is promoted to the type of the other as described 
    above in 4.2 Arithmetic operators on numeric values.

which should probably be "promoted or substituted", though the result is boolean here, so it is of minor significance.
Comment 1 Abel Braaksma 2015-03-23 18:12:50 UTC
(Raised against 3.0, and I see now the text is rewritten in 3.1, but I can't find the relevant bug, so possibly this should be closed with no action)

The argument on the meaning of conversion (is it substitution or promotion) is still valid in FO31 (internal draft).
Comment 2 Abel Braaksma 2015-03-23 18:25:15 UTC
s/xs:anyAtomicType/xs:untypedAtomic/
Comment 3 Michael Kay 2015-03-23 18:33:28 UTC
Appears to be a duplicate of bug #26453.

>The argument on the meaning of conversion (is it substitution or promotion) is still valid in FO31 (internal draft).

I don't think so. The term "conversion" is defined within the rules for the min and max functions: "The resulting sequence is referred to below as the converted sequence. ".
Comment 4 Abel Braaksma 2015-03-24 12:48:24 UTC
> I don't think so. The term "conversion" is defined within the rules for the 
> min and max functions:

I think you are right, and that I was confused between FO30 and FO31. Just to summarize my understanding of the new wording:

In 3.1 we now use the terms "is cast to" and "is instance of". In that case, it is my understanding that (xs:byte(10), xs:integer(8)) is treated, for the sake of the new section, as (xs:decimal(10), xs:decimal(8)), but not cast as. Since the derived xs:byte and xs:integer are all instances of xs:decimal, I understand that max((xs:byte(10), xs:integer(8)) returns xs:byte(10).

* Any numeric sequence containing xs:float but not xs:double will return xs:float. 

* Any numeric sequence containing xs:double will return xs:double.

* Any other numeric sequence (only instances of xs:decimal) will return the original type of the max item, no conversion takes place.

This also means that my test case above, max((xs:integer(12), xs:decimal(10))), will return xs:integer, not xs:decimal (I haven't tested it with the most recent Saxon, it may be fixed already).

My apologies for raising the bug, I think it can be closed with no action.

Should any tests in FO30 be treated in regards to the new wording of FO31? It would get really messy if we would try to allow possible variants of the interpretation of the original FO30 text.
Comment 5 Abel Braaksma 2015-03-24 14:28:12 UTC
From bug 26453, MKay wrote:

    If anyone doesn't believe this is an improvement, then I threaten 
    to write some tests for the edge cases under the current rule and watch 
    implementations break...

Now, I do think very strongly that this is an improvement, but it is quite possible to write a test that fails in older implementations and succeeds in newer, depending on the interpretation of the FO30 text:

max((xs:integer(12), xs:decimal(10))) instance of xs:integer

will return FALSE in FO30 (tested with Saxon) but will return TRUE with the new text of FO31.

I highly doubt that any existing code out there will rely on this behavior. I can't find tests that break (90% tests conversion to xs:double), but there are some tests that test LCT, for example:

> max((1.0, 1, 1, 1, 1)) instance of xs:decimal
>> change it to xs:integer and the outcome is debatable (items are equal)
>> the NOTE says this explicitly though

> let $var := max((xs:long(20),xs:short(13))) return $var instance of xs:integer
>> change it to "max((xs:long(10),xs:short(13))) instance of xs:short" and 
>> it was false (FO30) is true (FO31)

I do not know if this warrants a comment in the changes section of the spec, but it is a change that in rare edge cases has different results.
Comment 6 Michael Kay 2015-03-24 14:54:12 UTC
Abel wrote:

>max((xs:integer(12), xs:decimal(10))) instance of xs:integer

>will return FALSE in FO30 (tested with Saxon) but will return TRUE with the new
text of FO31.

The result of this test is implementation-dependent in FO30. The spec guarantees that you will get an xs:decimal, but an implementation is always permitted to return a subtype of what the spec requires, so returning an xs:integer (and thus returning TRUE) is permitted. 

>I do not know if this warrants a comment in the changes section of the spec, but it is a change that in rare edge cases has different results.

There is already such a comment in Appendix G.
Comment 7 Abel Braaksma 2016-01-19 16:06:26 UTC
(In reply to Michael Kay from comment #6)
> There is already such a comment in Appendix G.
Should we move forward and close this bug?
Comment 8 Andrew Coleman 2016-01-26 16:27:23 UTC
The joint WG agreed to close this with no action