Difference between revisions of "Higher-Order Functions"

From BaseX Documentation
Jump to navigation Jump to search
m (Text replacement - "syntaxhighlight" to "pre")
 
(9 intermediate revisions by the same user not shown)
Line 11: Line 11:
 
The most general function type is <code>function(*)</code>. It's the type of all function items. The following query for example goes through a list of XQuery items and, if it is a function item, prints its arity:
 
The most general function type is <code>function(*)</code>. It's the type of all function items. The following query for example goes through a list of XQuery items and, if it is a function item, prints its arity:
  
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
for $item in (1, 'foo', fn:concat#3, function($a) { 42 * $a })
 
for $item in (1, 'foo', fn:concat#3, function($a) { 42 * $a })
 
where $item instance of function(*)
 
where $item instance of function(*)
Line 20: Line 20:
 
The notation for specifying argument and return types is quite intuitive, as it closely resembles the function declaration. The XQuery function
 
The notation for specifying argument and return types is quite intuitive, as it closely resembles the function declaration. The XQuery function
  
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:char-at(
 
declare function local:char-at(
   $str as xs:string,
+
   $str as xs:string,
   $pos as xs:integer
+
   $pos as xs:integer
 
) as xs:string {
 
) as xs:string {
 
   fn:substring($str, $pos, 1)
 
   fn:substring($str, $pos, 1)
Line 33: Line 33:
 
Function types can also be nested. As an example we take <code>local:on-sequences</code>, which takes a function defined on single items and makes it work on sequences as well:
 
Function types can also be nested. As an example we take <code>local:on-sequences</code>, which takes a function defined on single items and makes it work on sequences as well:
  
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:on-sequences(
 
declare function local:on-sequences(
 
   $fun as function(item()) as item()*
 
   $fun as function(item()) as item()*
Line 58: Line 58:
  
 
{|
 
{|
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|fn:for-each|$seq as item()*, $function as function(item()) as item()*)|item()*}}
+
|<pre>fn:for-each(
|-
+
  $input  as item()*,
 +
  $action  as function(item()) as item()*
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Applies the specified <code>$function</code> to every item of <code>$seq</code> and returns all results as a single sequence.
+
|Applies the specified <code>$action</code> to every item of <code>$input</code> and returns all results as a single sequence.
|-
+
|- valign="top"
 
| '''Examples'''
 
| '''Examples'''
 
|
 
|
 
<ul><li>Square all numbers from 1 to 10:
 
<ul><li>Square all numbers from 1 to 10:
  <pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:for-each(1 to 10, math:pow(?, 2))
 
fn:for-each(1 to 10, math:pow(?, 2))
 
</pre>
 
</pre>
Line 74: Line 77:
 
</li>
 
</li>
 
<li>Apply a list of functions to a string:
 
<li>Apply a list of functions to a string:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $fs := (
 
let $fs := (
 
   fn:upper-case#1,
 
   fn:upper-case#1,
Line 85: Line 88:
 
</li>
 
</li>
 
<li>Process each item of a sequence with the arrow operator:
 
<li>Process each item of a sequence with the arrow operator:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
("one", "two", "three") => fn:for-each(fn:upper-case(?))
 
("one", "two", "three") => fn:for-each(fn:upper-case(?))
 
</pre>
 
</pre>
 
''Result:'' <code>ONE TWO THREE</code>
 
''Result:'' <code>ONE TWO THREE</code>
 
</li></ul>
 
</li></ul>
|-
+
|- valign="top"
 
| '''XQuery 1.0'''
 
| '''XQuery 1.0'''
 
|At the core, for-each is nothing else than a simple FLWOR expression:
 
|At the core, for-each is nothing else than a simple FLWOR expression:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:for-each(
 
declare function local:for-each(
 
   $seq as item()*,
 
   $seq as item()*,
Line 107: Line 110:
  
 
{|
 
{|
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|fn:filter|$seq as item()*, $pred as function(item()) as xs:boolean)|item()*}}
+
|<pre>fn:filter(
|-
+
  $input      as item()*,
 +
  $predicate  as function(item()) as xs:boolean
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Applies the boolean predicate <code>$pred</code> to all elements of the sequence <code>$seq</code>, returning those for which it returns <code>true()</code>.
+
|Applies the boolean <code>$predicate</code> to all elements of the sequence <code>$input</code>, returning those for which it returns <code>true()</code>.
|-
+
|- valign="top"
 
| '''Examples'''
 
| '''Examples'''
 
|<ul><li>All even integers until 10:
 
|<ul><li>All even integers until 10:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:filter(1 to 10, function($x) { $x mod 2 eq 0 })
 
fn:filter(1 to 10, function($x) { $x mod 2 eq 0 })
 
</pre>
 
</pre>
Line 122: Line 128:
 
</li>
 
</li>
 
<li>Strings that start with an upper-case letter:
 
<li>Strings that start with an upper-case letter:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $first-upper := function($str) {
 
let $first-upper := function($str) {
 
   let $first := fn:substring($str, 1, 1)
 
   let $first := fn:substring($str, 1, 1)
Line 132: Line 138:
 
</li>
 
</li>
 
<li>Inefficient prime number generator:
 
<li>Inefficient prime number generator:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $is-prime := function($x) {
 
let $is-prime := function($x) {
 
   $x gt 1 and (every $y in 2 to ($x - 1) satisfies $x mod $y ne 0)
 
   $x gt 1 and (every $y in 2 to ($x - 1) satisfies $x mod $y ne 0)
Line 140: Line 146:
 
''Result:'' <code>2 3 5 7 11 13 17 19</code>
 
''Result:'' <code>2 3 5 7 11 13 17 19</code>
 
</li></ul>
 
</li></ul>
|-
+
|- valign="top"
 
| '''Note'''
 
| '''Note'''
 
|<code>fn:filter</code> can be easily implemented with <code>fn:for-each</code>:
 
|<code>fn:filter</code> can be easily implemented with <code>fn:for-each</code>:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:filter($seq, $pred) {
 
declare function local:filter($seq, $pred) {
 
   for-each(
 
   for-each(
Line 153: Line 159:
 
};
 
};
 
</pre>
 
</pre>
|-
+
|- valign="top"
 
| '''XQuery 1.0'''
 
| '''XQuery 1.0'''
 
|At the core, for-each is nothing else than a filter expression:
 
|At the core, for-each is nothing else than a filter expression:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:filter(
 
declare function local:filter(
 
   $seq as item()*,
 
   $seq as item()*,
Line 169: Line 175:
  
 
{|
 
{|
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|fn:for-each-pair|$seq1 as item()*, $seq2 as item()*, $function as function(item(), item()) as item()*|item()*}}
+
|<pre>fn:for-each-pair(
|-
+
  $input1  as item()*,
 +
  $input2  as item()*,
 +
  $action  as function(item(), item()) as item()*
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Applies the specified {{Code|$function}} to the successive pairs of items of <code>$seq1</code> and <code>$seq2</code>. Evaluation is stopped if one sequence yields no more items.
+
|Applies the specified {{Code|$action}} to the successive pairs of items of <code>$input1</code> and <code>$input2</code>. Evaluation is stopped if one sequence yields no more items.
|-
+
|- valign="top"
 
| '''Examples'''
 
| '''Examples'''
 
|<ul><li>Adding one to the numbers at odd positions:
 
|<ul><li>Adding one to the numbers at odd positions:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:for-each-pair(
 
fn:for-each-pair(
 
   fn:for-each(1 to 10, function($x) { $x mod 2 }),
 
   fn:for-each(1 to 10, function($x) { $x mod 2 }),
Line 188: Line 198:
 
</li>
 
</li>
 
<li>Line numbering:
 
<li>Line numbering:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $number-words := function($str) {
 
let $number-words := function($str) {
 
   fn:string-join(
 
   fn:string-join(
Line 202: Line 212:
 
</pre>
 
</pre>
 
''Result:''
 
''Result:''
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
1: how
 
1: how
 
2: are
 
2: are
Line 209: Line 219:
 
</li>
 
</li>
 
<li>Checking if a sequence is sorted:
 
<li>Checking if a sequence is sorted:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $is-sorted := function($seq) {
 
let $is-sorted := function($seq) {
 
   every $b in
 
   every $b in
 
     fn:for-each-pair(
 
     fn:for-each-pair(
 
       $seq,
 
       $seq,
       fn:tail($seq),
+
       tail($seq),
 
       function($a, $b) { $a le $b }
 
       function($a, $b) { $a le $b }
 
     )
 
     )
Line 225: Line 235:
 
</pre>
 
</pre>
 
''Result:'' <code>true false</code></li></ul>
 
''Result:'' <code>true false</code></li></ul>
|-
+
|- valign="top"
 
| '''XQuery 1.0'''
 
| '''XQuery 1.0'''
|<pre class="brush:xquery">
+
|<pre lang='xquery'>
 
declare function local:for-each-pair(
 
declare function local:for-each-pair(
 
   $seq1 as item()*,
 
   $seq1 as item()*,
Line 247: Line 257:
 
Calculating the ''product'' of a sequence of integers for example is easy in <code>Java</code>:
 
Calculating the ''product'' of a sequence of integers for example is easy in <code>Java</code>:
  
<pre class="brush:java">
+
<pre lang="java">
 
public int product(int[] seq) {
 
public int product(int[] seq) {
 
   int result = 1;
 
   int result = 1;
Line 264: Line 274:
  
 
{|
 
{|
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|fn:fold-left|$seq as item()*, $seed as item()*, $fun as function(item()*, item()) as item()*|item()*}}
+
|<pre>fn:fold-left(
|-
+
  $input  as item()*,
 +
  $zero    as item()*,
 +
  $action  as function(item()*, item()) as item()*
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|The ''left fold'' traverses the sequence from the left.
+
|The ''left fold'' traverses the {{Code|$input}} from the left.
The query <code>fn:fold-left(1 to 5, 0, $f)</code> for example would be evaluated as:
+
The query <code>fn:fold-left(1 to 5, 0, $f)</code>, for example, would be evaluated as:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
$f($f($f($f($f(0, 1), 2), 3), 4), 5)
 
$f($f($f($f($f(0, 1), 2), 3), 4), 5)
 
</pre>
 
</pre>
|-
+
|- valign="top"
 
| '''Examples'''
 
| '''Examples'''
 
|<ul><li>Product of a sequence of integers:
 
|<ul><li>Product of a sequence of integers:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:fold-left(1 to 5, 1,
 
fn:fold-left(1 to 5, 1,
 
   function($result, $curr) { $result * $curr }
 
   function($result, $curr) { $result * $curr }
Line 285: Line 299:
 
</li>
 
</li>
 
<li>Illustrating the evaluation order:
 
<li>Illustrating the evaluation order:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:fold-left(1 to 5, '$seed',
 
fn:fold-left(1 to 5, '$seed',
 
   concat('$f(', ?, ', ', ?, ')')
 
   concat('$f(', ?, ', ', ?, ')')
Line 293: Line 307:
 
</li>
 
</li>
 
<li>Building a decimal number from digits:
 
<li>Building a decimal number from digits:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $from-digits := fold-left(?, 0,
 
let $from-digits := fold-left(?, 0,
 
   function($n, $d) { 10 * $n + $d }
 
   function($n, $d) { 10 * $n + $d }
Line 304: Line 318:
 
''Result:'' <code>12345 42</code>
 
''Result:'' <code>12345 42</code>
 
</li></ul>
 
</li></ul>
|-
+
|- valign="top"
 
| '''XQuery 1.0'''
 
| '''XQuery 1.0'''
 
|As folds are more general than ''FLWOR'' expressions, the implementation isn't as concise as the former ones:
 
|As folds are more general than ''FLWOR'' expressions, the implementation isn't as concise as the former ones:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
declare function local:fold-left(
 
declare function local:fold-left(
   $seq as item()*,
+
   $input  as item()*,
   $seed as item()*,
+
   $zero    as item()*,
   $fun as function(item()*, item()) as item()*
+
   $action  as function(item()*, item()) as item()*
 
) as item()* {
 
) as item()* {
   if(empty($seq)) then $seed
+
   if(empty($input)) then $zero
 
   else local:fold-left(
 
   else local:fold-left(
     fn:tail($seq),
+
     tail($input),
     $fun($seed, fn:head($seq)),
+
     $action($zero, head($input)),
     $fun
+
     $action
 
   )  
 
   )  
 
};
 
};
Line 326: Line 340:
  
 
{|
 
{|
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|fn:fold-right|$seq as item()*, $seed as item()*, $fun as function(item(), item()*) as item()*|item()*}}
+
|<pre>fn:fold-right(
|-
+
  $input  as item()*,
 +
  $zero    as item()*,
 +
  $action  as function(item(), item()*) as item()*
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|The ''right fold'' <code>fn:fold-right($seq, $seed, $fun)</code> traverses the sequence from the right.
+
|The ''right fold'' traverses the {{Code|$input}} from the right.
The query <code>fn:fold-right(1 to 5, 0, $f)</code> for example would be evaluated as:
+
The query <code>fn:fold-right(1 to 5, 0, $f)</code>, for example, would be evaluated as:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
$f(1, $f(2, $f(3, $f(4, $f(5, 0)))))
 
$f(1, $f(2, $f(3, $f(4, $f(5, 0)))))
 
</pre>
 
</pre>
|-
+
|- valign="top"
 
| '''Examples'''
 
| '''Examples'''
 
|<ul><li>Product of a sequence of integers:
 
|<ul><li>Product of a sequence of integers:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:fold-right(1 to 5, 1,
 
fn:fold-right(1 to 5, 1,
 
   function($curr, $result) { $result * $curr }
 
   function($curr, $result) { $result * $curr }
Line 347: Line 365:
 
</li>
 
</li>
 
<li>Illustrating the evaluation order:
 
<li>Illustrating the evaluation order:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
fn:fold-right(1 to 5, '$seed',
 
fn:fold-right(1 to 5, '$seed',
 
   concat('$f(', ?, ', ', ?, ')')
 
   concat('$f(', ?, ', ', ?, ')')
Line 355: Line 373:
 
</li>
 
</li>
 
<li>Reversing a sequence of items:
 
<li>Reversing a sequence of items:
<pre class="brush:xquery">
+
<pre lang='xquery'>
 
let $reverse := fn:fold-right(?, (),
 
let $reverse := fn:fold-right(?, (),
 
   function($item, $rev) {
 
   function($item, $rev) {
Line 365: Line 383:
 
''Result:'' <code>10 9 8 7 6 5 4 3 2 1</code>
 
''Result:'' <code>10 9 8 7 6 5 4 3 2 1</code>
 
</li></ul>
 
</li></ul>
|-
+
|- valign="top"
 
| '''XQuery 1.0'''
 
| '''XQuery 1.0'''
|<pre class="brush:xquery">
+
|<pre lang='xquery'>
 
declare function local:fold-right(
 
declare function local:fold-right(
   $seq as item()*,
+
   $input  as item()*,
   $seed as item()*,
+
   $zero    as item()*,
   $fun as function(item(), item()*) as item()*
+
   $action  as function(item(), item()*) as item()*
 
) as item()* {
 
) as item()* {
   if(empty($seq)) then $seed
+
   if(empty($input)) then $zero
   else $fun(
+
   else $action(
     fn:head($seq),
+
     head($input),
     local:fold-right(tail($seq), $seed, $fun)
+
     local:fold-right(tail($input), $zero, $action)
 
   )
 
   )
 
};
 
};

Latest revision as of 18:39, 1 December 2023

This page present some higher-order functions of the XQuery specification. The BaseX-specific Higher-Order Functions Module contains some additional useful functions.

Function Items[edit]

Probably the most important new feature in XQuery 3.0 are function items, i. e., items that act as functions, but can also be passed to and from other functions and expressions. This feature makes functions first-class citizens of the language. The XQuery 3.0 page goes into details on how function items can be obtained.

Function Types[edit]

Like every XQuery item, function items have a sequence type. It can be used to specify the arity (number of arguments the function takes) and the argument and result types.

The most general function type is function(*). It's the type of all function items. The following query for example goes through a list of XQuery items and, if it is a function item, prints its arity:

for $item in (1, 'foo', fn:concat#3, function($a) { 42 * $a })
where $item instance of function(*)
return fn:function-arity($item)

Result: 3 1

The notation for specifying argument and return types is quite intuitive, as it closely resembles the function declaration. The XQuery function

declare function local:char-at(
  $str  as xs:string,
  $pos  as xs:integer
) as xs:string {
  fn:substring($str, $pos, 1)
};

for example has the type function(xs:string, xs:integer) as xs:string. It isn't possible to specify only the argument and not the result type or the other way round. A good place-holder to use when no restriction is wanted is item()*, as it matches any XQuery value.

Function types can also be nested. As an example we take local:on-sequences, which takes a function defined on single items and makes it work on sequences as well:

declare function local:on-sequences(
  $fun as function(item()) as item()*
) as function(item()*) as item()* {
  fn:for-each($fun, ?)
};

We willl see later how fn:for-each(...) works. The type of local:on-sequences(...) on the other hand is easily constructed, if a bit long:

function(function(item()) as item()*) as function(item()*) as item()*.

Higher-Order Functions[edit]

A higher-order function is a function that takes other functions as arguments and/or returns them as results. fn:for-each and local:on-sequences from the last chapter are nice examples.

With the help of higher-order functions, one can extract common patterns of behavior and abstract them into a library function.

Sequences[edit]

Some usage patterns on sequences are so common that the higher-order functions describing them are in the XQuery standard libraries. They are listed here, together with their possible XQuery implementation and some motivating examples.

fn:for-each[edit]

Signature
fn:for-each(
  $input   as item()*,
  $action  as function(item()) as item()*
) as item()*
Summary Applies the specified $action to every item of $input and returns all results as a single sequence.
Examples
  • Square all numbers from 1 to 10:
    fn:for-each(1 to 10, math:pow(?, 2))
    

    Result: 1 4 9 16 25 36 49 64 81 100

  • Apply a list of functions to a string:
    let $fs := (
      fn:upper-case#1,
      fn:substring(?, 4),
      fn:string-length#1
    )
    return fn:for-each($fs, function($f) { $f('foobar') })
    

    Result: FOOBAR bar 6

  • Process each item of a sequence with the arrow operator:
    ("one", "two", "three") => fn:for-each(fn:upper-case(?))
    

    Result: ONE TWO THREE

XQuery 1.0 At the core, for-each is nothing else than a simple FLWOR expression:
declare function local:for-each(
  $seq as item()*,
  $fun as function(item()) as item()*
) as item()* {
  for $s in $seq
  return $fun($s)
};

fn:filter[edit]

Signature
fn:filter(
  $input      as item()*,
  $predicate  as function(item()) as xs:boolean
) as item()*
Summary Applies the boolean $predicate to all elements of the sequence $input, returning those for which it returns true().
Examples
  • All even integers until 10:
    fn:filter(1 to 10, function($x) { $x mod 2 eq 0 })
    

    Result: 2 4 6 8 10

  • Strings that start with an upper-case letter:
    let $first-upper := function($str) {
      let $first := fn:substring($str, 1, 1)
      return $first eq fn:upper-case($first)
    }
    return fn:filter(('FooBar', 'foo', 'BAR'), $first-upper)
    

    Result: FooBar BAR

  • Inefficient prime number generator:
    let $is-prime := function($x) {
      $x gt 1 and (every $y in 2 to ($x - 1) satisfies $x mod $y ne 0)
    }
    return filter(1 to 20, $is-prime)
    

    Result: 2 3 5 7 11 13 17 19

Note fn:filter can be easily implemented with fn:for-each:
declare function local:filter($seq, $pred) {
  for-each(
    $seq,
    function($x) {
      if($pred($x)) then $x else ()
    }
  )
};
XQuery 1.0 At the core, for-each is nothing else than a filter expression:
declare function local:filter(
  $seq as item()*,
  $pred as function(item()) as xs:boolean
) as item()* {
  $seq[$pred(.)]
};

fn:for-each-pair[edit]

Signature
fn:for-each-pair(
  $input1   as item()*,
  $input2   as item()*,
  $action  as function(item(), item()) as item()*
) as item()*
Summary Applies the specified $action to the successive pairs of items of $input1 and $input2. Evaluation is stopped if one sequence yields no more items.
Examples
  • Adding one to the numbers at odd positions:
    fn:for-each-pair(
      fn:for-each(1 to 10, function($x) { $x mod 2 }),
      (1, 1, 1, 1, 1),
      function($a, $b) { $a + $b }
    )
    

    Result: 2 1 2 1 2

  • Line numbering:
    let $number-words := function($str) {
      fn:string-join(
        fn:for-each-pair(
          1 to 1000000000,
          tokenize($str, ' +'),
          concat(?, ': ', ?)
        ),
        '
    '
      )
    }
    return $number-words('how are you?')
    

    Result:

    1: how
    2: are
    3: you?
    
  • Checking if a sequence is sorted:
    let $is-sorted := function($seq) {
      every $b in
        fn:for-each-pair(
          $seq,
          tail($seq),
          function($a, $b) { $a le $b }
        )
      satisfies $b
    }
    return (
      $is-sorted(1 to 10),
      $is-sorted((1, 2, 42, 4, 5))
    )
    
    Result: true false
XQuery 1.0
declare function local:for-each-pair(
  $seq1 as item()*,
  $seq2 as item()*,
  $fun as function(item(), item()) as item()*
) as item()* {
  for $pos in 1 to min((count($seq1), count($seq2)))
  return $fun($seq1[$pos], $seq2[$pos])
};

Folds[edit]

A fold, also called reduce or accumulate in other languages, is a very basic higher-order function on sequences. It starts from a seed value and incrementally builds up a result, consuming one element from the sequence at a time and combining it with the aggregate of a user-defined function.

Folds are one solution to the problem of not having state in functional programs. Solving a problem in imperative programming languages often means repeatedly updating the value of variables, which isn't allowed in functional languages.

Calculating the product of a sequence of integers for example is easy in Java:

public int product(int[] seq) {
  int result = 1;
  for(int i : seq) {
    result = result * i;
  }
  return result;
}

Nice and efficient implementations using folds will be given below.

The linear folds on sequences come in two flavors. They differ in the direction in which they traverse the sequence:

fn:fold-left[edit]

Signature
fn:fold-left(
  $input   as item()*,
  $zero    as item()*,
  $action  as function(item()*, item()) as item()*
) as item()*
Summary The left fold traverses the $input from the left.

The query fn:fold-left(1 to 5, 0, $f), for example, would be evaluated as:

$f($f($f($f($f(0, 1), 2), 3), 4), 5)
Examples
  • Product of a sequence of integers:
    fn:fold-left(1 to 5, 1,
      function($result, $curr) { $result * $curr }
    )
    

    Result: 120

  • Illustrating the evaluation order:
    fn:fold-left(1 to 5, '$seed',
      concat('$f(', ?, ', ', ?, ')')
    )
    

    Result: $f($f($f($f($f($seed, 1), 2), 3), 4), 5)

  • Building a decimal number from digits:
    let $from-digits := fold-left(?, 0,
      function($n, $d) { 10 * $n + $d }
    )
    return (
      $from-digits(1 to 5),
      $from-digits((4, 2))
    )
    

    Result: 12345 42

XQuery 1.0 As folds are more general than FLWOR expressions, the implementation isn't as concise as the former ones:
declare function local:fold-left(
  $input   as item()*,
  $zero    as item()*,
  $action  as function(item()*, item()) as item()*
) as item()* {
  if(empty($input)) then $zero
  else local:fold-left(
    tail($input),
    $action($zero, head($input)),
    $action
  ) 
};

fn:fold-right[edit]

Signature
fn:fold-right(
  $input   as item()*,
  $zero    as item()*,
  $action  as function(item(), item()*) as item()*
) as item()*
Summary The right fold traverses the $input from the right.

The query fn:fold-right(1 to 5, 0, $f), for example, would be evaluated as:

$f(1, $f(2, $f(3, $f(4, $f(5, 0)))))
Examples
  • Product of a sequence of integers:
    fn:fold-right(1 to 5, 1,
      function($curr, $result) { $result * $curr }
    )
    

    Result: 120

  • Illustrating the evaluation order:
    fn:fold-right(1 to 5, '$seed',
      concat('$f(', ?, ', ', ?, ')')
    )
    

    Result: $f(1, $f(2, $f(3, $f(4, $f(5, $seed)))))

  • Reversing a sequence of items:
    let $reverse := fn:fold-right(?, (),
      function($item, $rev) {
        $rev, $item
      }
    )
    return $reverse(1 to 10)
    

    Result: 10 9 8 7 6 5 4 3 2 1

XQuery 1.0
declare function local:fold-right(
  $input   as item()*,
  $zero    as item()*,
  $action  as function(item(), item()*) as item()*
) as item()* {
  if(empty($input)) then $zero
  else $action(
    head($input),
    local:fold-right(tail($input), $zero, $action)
  )
};

Note that the order of the arguments of $fun are inverted compared to that in fn:fold-left(...).