The if-then-else behaviour of rules, whereby no further rules are processed once one has fired, is implemented using simple tail recursion:
declare function eval:rules(
$rules as element(sch:rule)*,
$prolog as xs:string?,
$context as map(*)
)
as element()*
{
if(empty($rules))
then ()
else
let $result := eval:rule(head($rules), $prolog, $context)
return if($result)
then $result
else eval:rules(tail($rules), $prolog, $context)
};where the result of the eval:rule() call will be the empty sequence if no matching rule context exists.