Search code examples
xslt-3.0

unbounded sequence - tail recursion


I want to create an unbounded sequence and wondered if i could do it via a tail recursive function.

a simple example being

<xsl:function name="kooks:repeat" as="xs:integer*">
    <xsl:sequence select="1"/>
    <xsl:sequence select="kooks:repeat()"/>
</xsl:function>

<xsl:template match="/">
    <xsl:sequence select="kooks:repeat()[5]"/>
</xsl:template>

(I'm using Saxon-ee 11.4)

This fails with

Severity: error
Description: class java.lang.StackOverflowError

I think the issue is because there are 2 sequence statements then saxon interprets this as a append/cons operation and thus the function is no longer tail recursive (in its model).

If we compare this to the F# code (which is what I'm trying to translate into XSLT)

let rec ones () =
    seq {
        yield 1
        yield! ones ()
    }

let tenMillionthEntry = 
    ones ()
    |> Seq.skip 10000000
    |> Seq.take 1
    |> Seq.head

this doesn't consider the consecutive yield, yield! as an operation in the function and so its tail recursion optimisation works (presumably as this code doesnt blow up).


Solution

  • Christian Grün on the XML slack came up with something along the following lines:

    let $inf-rec := function($start, $self) {
          map {
            'skip': function($c) { $self($start + $c, $self) },
            'take': function($c) { $start to $start + $c - 1 }
          }
        },
        $inf := function() {
          $inf-rec(1, $inf-rec)
        }
    return $inf()?skip(10000000)?take(1) ! head(.)
    

    XPath 3.1 example online.

    He also said in XPath 4.0 there are functions https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-do-until and https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-while-do that could help.

    Also wait whether Dimitre Novatchev shows up to explain his ideas for infinite sequences in XPath 4.