Почему числовой литерал никогда не может иметь тип 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))