I can't find an explanation on the following. I've made this test script in order to solve my previous question.
xquery version "3.0" ;
declare default function namespace 'local' ;
declare function local:is-img-only( $element as element() ) as xs:boolean {
($element/child::*[1] instance of element(img))
and (fn:not($element/child::*[2]))
and (fn:normalize-space($element) = '')
} ;
let $in-xml := <myxml>
<p id="1">
<img id="1"/>
</p>
<p id="2">
<img id="1"/>
hello
</p>
<p id="3">
<img id="1"/>
</p>
<p id="4">
<blockquote>hello</blockquote>
<img id="1"/>
</p>
<p id="5">
<img id="1"/>
<img id="2"/>
</p>
</myxml>
Then, the following using if then else
:
for $p in $in-xml/p
return if (local:is-img-only($p))
then $p/@id/fn:data() || ' has only an img child'
else $p/@id/fn:data() || ' has not strictly an img child'
returns as expected :
1 has only an img child
2 has not strictly an img child
3 has only an img child
4 has not strictly an img child
5 has not strictly an img child
Whereas the following using switch case
for $p in $in-xml/p
return switch ($p)
case (local:is-img-only($p)) return $p/@id/fn:data() || ' has only an img child'
default return $p/@id/fn:data() || ' has not strictly an img child'
returns as not expected :
1 has not strictly an img child
2 has not strictly an img child
3 has not strictly an img child
4 has not strictly an img child
5 has not strictly an img child
Any explanation ? Why Conditional Expressions would not behave the same way than Switch Expressions ?
Great job in locating the relevant sections of the XQuery specification for your question! That's half the battle. Switch expressions do share some things in common with conditional expressions, but there are some differences.
The key difference is that a conditional expression evaluates a test expression to see if the result is true or false, while a switch expression compares one expression against one or more other expressions to find the first pair that are equal. Using pseudo code, we can illustrate the difference. All of the logic in a conditional expression takes place here on one line:
if ($EXPRESSION)
In contrast, the logic of a switch expression is spread over many lines:
switch ($EXPRESSION_A)
case ($EXPRESSION_B) ...
case ($EXPRESSION_C) ...
case ($EXPRESSION_D) ...
default ...
The switch expression is actually performing a chain of comparisons, which we could express as even more lines of conditional expressions:
if (deep-equal($EXPRESSION_A, $EXPRESSION_B))
then ...
else
if (deep-equal($EXPRESSION_A, $EXPRESSION_C))
then ...
else ...
if (deep-equal($EXPRESSION_A, $EXPRESSION_D))
then ...
else ... (: "default" :)
The nitty gritty differences between these two expressions are described in the spec, starting with where they describe "the first step" in processing each expression. Whereas the first step in processing a conditional expression is:
to find the effective boolean value of the test expression.
... the first step in processing a switch expression is:
to apply atomization to the value of the switch operand expression.
Let's return to your concrete example and take a look at your conditional's test expression and your switch's switch operand expression:
Your conditional's test expression:
if (local:is-img-only($p))
Your switch's operand expression:
switch ($p)
The conditional's test expression returns a boolean - true()
or false()
, so this condition clearly charts out the path of the rest of the code.
In contrast, the logic of the switch expression has only just begun with this operand expression. First, it finds the atomized value of the operand expression—the <p>
element bound to the $p
variable in your FLWOR expression. Since this value depends on which <p>
we're looking at, the atomized value will either be an empty string (""
), whitespace, or "hello"
(or some combination thereof, depending on whitespace in your source and your boundary space declaration). Then, the switch's first case operand is evaluated and atomized. Your first case operand is as follows:
case (local:is-img-only($p))
This expression, as we'll recall, evaluates to a boolean. The next step the switch expression performs is to compare the atomized value of the switch operand expression with the atomized value of the switch case operand using the fn:deep-equal
function. Effectively, then, we're asking the XQuery processor to perform the following comparisons:
deep-equal("", true())
deep-equal("hello", false())
In both cases the comparison returns false()
. Thus, this comparison in our case operand always fails, so the switch expression is falling back on the default
clause in every iteration of your FLWOR expression.
A switch expression that mimics the results of your original conditional expression would be the following:
for $p in $in-xml/p
return switch(local:is-img-only($p))
case (true()) return $p/@id/fn:data() || ' has only an img child'
default return $p/@id/fn:data() || ' has not strictly an img child'
This performs the following checks:
deep-equal(true(), true())
deep-equal(true(), false())
And returns the identical results as your conditional expression.
This isn't a particularly compelling use for the switch expression - since we're effectively evaluating a single test expression. The switch expression really shines when you have many values to compare. The spec offers us a good example switch expression to consider:
switch ($animal)
case "Cow" return "Moo"
case "Cat" return "Meow"
case "Duck" return "Quack"
default return "What's that odd noise?"
This is far more readable and compact than the equivalent conditional expression:
if (deep-equal($animal, "Cow"))
then "Moo"
else
if (deep-equal($animal, "Cat"))
then "Meow"
else
if (deep-equal($animal, "Duck"))
then "Quack"
else "What's that odd noise?"
Or even the more straight-forward interpretation:
if ($animal eq "Cow")
then "Moo"
else
if ($animal eq "Cat")
then "Meow"
else
if ($animal eq "Duck")
then "Quack"
else "What's that odd noise?"
The upshot: if you ever find yourself writing a chain of conditionals, and the left side of the comparison is always the same, consider making a switch.