Почему числовой литерал никогда не может иметь тип xs: positiveInteger в XQuery?

Я заметил небольшие различия в том, как реализации XQuery обрабатывают (под) типы. В частности, обработка буквальных чисел в качестве входных данных для функций, для которых объявлен принятый тип ввода. Я наивно думал, что любой числовой литерал, который может быть приведен к этому конкретному числовому типу, будет принят.

declare function local:any ($n as xs:anyAtomic) { $n };
declare function local:decimal ($n as xs:decimal) { $n };
declare function local:integer ($n as xs:integer) { $n };
declare function local:pos-int ($n as xs:positiveInteger) { $n };

local:any(1), (: works :)
local:decimal(1), (: works :)
local:integer(1), (: works :)
local:pos-int(1)  (: throws in all tested implementations :)

exist-db позволяет xs:long,xs:int,... Саксонский нет.

Я не смог найти никаких причин для такого поведения ни в спецификации Xquery Spec 2.5.5 SequenceType Matching, ни в спецификации функций Xpath 1.6.3 Иерархия атомарных типов

Может ли кто-нибудь здесь пролить свет на то, почему Saxon 9.3.1 HE, BaseX 9.3.1 [Standalone] и eXist 5.3.0-SNAPSHOT ведут себя так?

Я просто пропустил ту часть спецификации, где определено, что буквальный 1приводится к xs:integer? xs:decimal как самый верхний тип имел бы больше смысла, но если разрешен один подтип, почему бы не пойти полностью?

вот живая демонстрация

3 ответа

Решение

Я думаю, что спецификация в этой области очень неудачна, но она ясна: значение - это xs:positiveIntegerтолько если он обозначен как таковой, а не просто потому, что он (а) целое число и (б) положительно. В рабочей группе XQuery по этому поводу велись долгие дискуссии с участием некоторых видных экспертов по системам типов языков программирования (например, Фила Вадлера), и это решение было принято. Мне самому это не понравилось.

Где это сказано в спецификации? Определения в спецификации XDM - хорошее начало:

https://www.w3.org/TR/xpath-datamodel-31/

[Определение: атомарное значение - это значение в пространстве значений атомарного типа, помеченное именем этого атомарного типа.]

[Определение: атомарный тип - это примитивный простой тип или тип, полученный путем ограничения из другого атомарного типа.] (Типы, производные списком или объединением, не являются атомарными.)

[Определение: примитивные простые типы - это типы, определенные в 2.1.1 Типы, принятые из схемы XML.]

Затем в §3.1.1 спецификации XQuery говорится о числовых литералах:

Значение числового литерала, не содержащего "." и ни один символ e или E не является атомарным значением типа xs:integer.

В §3.18.1 приводятся правила для оператора "instance of":

Логический оператор instance of возвращает истину, если значение его первого операнда совпадает с SequenceType во втором операнде, согласно правилам сопоставления SequenceType;

и §2.5.5.2 дает соответствующее правило для сопоставления SequenceType:

ItemType, состоящий просто из EQName, интерпретируется как AtomicOrUnionType. Ожидаемый тип AtomicOrUnionType соответствует атомарному значению, фактический тип которого - AT, если derives-from( AT, AtomicOrUnionType) истинно.

В совокупности получается, что выражение 3 instance of xs:positiveInteger возвращает false (потому что xs:integer не происходит от xs:positiveinteger).

Наконец, когда ожидаемый тип аргумента функции xs:positiveInteger, а вызов функции предоставляет значение 3, тогда в игру вступают правила преобразования функций из §3.1.5.2. Они позволяют различные преобразования из предоставленного значения в требуемый тип, но "понижающее преобразование" из xs: integer в xs:positiveInteger не входит в их число. Значит это ошибка:

Если после приведенных выше преобразований результирующее значение не соответствует ожидаемому типу в соответствии с правилами сопоставления SequenceType, возникает ошибка типа [err:XPTY0004].

Как я уже сказал, мне не нравятся правила, и я много раз пытался их изменить. Но они ясны, и любой продукт, который им не соответствует, не соответствует требованиям.

https://www.w3.org/TR/xpath-31/ указывает, какой тип продвижения разрешен:

Продвижение числового типа:

Значение типа xs:float (или любого типа, полученного ограничением из xs:float) может быть повышено до типа xs: double. Результатом является значение xs: double, которое совпадает с исходным значением.

Значение типа xs:decimal (или любой тип, полученный ограничением от xs:decimal) может быть повышен до любого из типов xs: float или xs: double. Результат этого продвижения создается путем приведения исходного значения к требуемому типу. Такое продвижение может привести к потере точности.

Для других типов вы должны явно использовать конструктор, например local:int(xs:int(1)).

Вы можете передать числовой литерал 1 к local:pos-int() функция в MarkLogic:

declare function local:any($n as xs:anyAtomicType ) { $n };
declare function local:decimal($n as xs:decimal) { $n };
declare function local:integer($n as xs:integer) { $n };
declare function local:pos-int($n as xs:positiveInteger) { $n };

local:any(1), (: works :)
local:decimal(1), (: works :)
local:integer(1), (: works :)
local:pos-int(1)  (: works fine in MarkLogic :)

И вы можете использовать xdmp:type() чтобы сообщить, что возвращаемое значение имеет тип positiveInteger

xquery version "1.0-ml";
declare function local:pos-int($n as xs:positiveInteger) { $n };
xdmp:type(local:pos-int(1))
Другие вопросы по тегам