Re: [xsl] Sequential numbers in pure xslt, breaking the no-side-effect rule

Subject: Re: [xsl] Sequential numbers in pure xslt, breaking the no-side-effect rule
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Fri, 16 Mar 2007 11:38:13 -0400
Hi,

It seems to me that the only reason this "can't be done" is that XSLT is designed specifically to insulate you from details of its execution, and one or another implementation of the language will honor that design feature to a greater or lesser extent.

I can see Andrew has made much the same point, in a different way, in his hack to counter your hack. The "hidden feature" of generate-id() you are using is, in fact, a feature of generate-id() only in Saxon, and not even quite the feature you are taking it to be, so it's really a variant of methods 1 and 2.

In other words, the impossibility of it is in its assumptions. "Create a function that returns '1' on first call, and returns one higher each next call" etc. requires a particular definition of "first call" that has no meaning in XSLT terms, making sense only within an execution context that has nothing to do with the XSLT transform as such.

Note: IANAM (I'm not a mathematician, or computer scientist, or anything of the sort).

Cheers,
Wendell

At 10:56 AM 3/16/2007, you wrote:
On 3/16/07, Abel Braaksma <abel.online@xxxxxxxxx> wrote:
Hi XSLT'ers,

AFAIK the answer to this is a simple, short "no, cannot be done". But I
wanted to check it with the specialists to be sure ;)

The request is simple: create a function that returns '1' on first call,
and returns one higher each next call, without overlaps or jumps,
without any input. Let's call it my:seq-number:

<xsl:function name="my:seq-number" as="xs:integer">
    <!-- some implementation -->
</xsl:function>

then the following line would return '1 2 3':
<xsl:value-of select="my:seq-number(), my:seq-number(), my:seq-number()" />

There are three scenarios to resolve this issue:

  1. Use an extension function in the host language (java, .net)
  2. Use an extension instruction like saxon:assign
  3. Use 'hidden features' of generate-id().

The first two are obvious. The last one is the best I good get with
"pure xslt", but as I tested, does not work with AltovaXML (in fact, it
is not even guaranteed to work on other or future versions of Saxon):

<xsl:function name="my:seq:number" as="xs:integer">
    <xsl:variable name="node"><xsl:comment /></xsl:variable>
    <xsl:sequence select="xs:integer(substring(generate-id($node), 2) -
1" />
</xsl:function>

this will indeed return 1,2,3 etc, provided that, depending on the order
of execution, the first node that is auto-created is this node, and that
no other nodes are created meanwhile. (FYI, Saxon numbers document nodes
as d1, d2 etc, where 'd1' is the input doc).

Doing this with AltovaXML generates too much randomness, i.e., the
output of generate-id is: idroot219951944 idroot219959376 etc (this is
not a non-comformance bug. Not being able to deal with empty
<xsl:comment /> is, however, but I am getting adrift...).

Am I overlooking something, or is this about the best I can get? I know
I am in violation and that requesting for functions/variables/templates
that return different results with the same input is asking for breaking
the rules.

Thanks for any extra insights on an already overly discussed subject,

Really interesting point Abel, but aren't you effectively hacking a feature of Saxon to get the results you want, and only then can you get them if you call that function without calling others in between - for example:

<xsl:stylesheet version="2.0"
xmlns:seq="seq"
xmlns:xs="http://www.w3.org/2001/XMLSchema";
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:function name="seq:number" as="xs:integer">
  <xsl:variable name="node"><xsl:comment /></xsl:variable>
  <xsl:sequence select="xs:integer(substring(generate-id($node), 2)) - 1" />
</xsl:function>

<xsl:function name="seq:number2" as="xs:integer?">
  <xsl:variable name="node"><xsl:comment /></xsl:variable>
  <xsl:message><xsl:copy-of select="$node"/></xsl:message>
  <xsl:sequence select="()"/>
</xsl:function>

<xsl:template match="/">
        <xsl:sequence select="for $x in (1 to 10) return
                if ($x mod 2 = 1) then
                        seq:number()
                else
                        seq:number2()"/>
</xsl:template>

</xsl:stylesheet>

This calls a different function in between calls to your function that
creates nodes which increment the number returned by generate-id() (it
uses xsl:message to force the variable to be evaluated) , which makes
the output:

1 4 7 10 13

cheers
andrew


======================================================================
Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.                http://www.mulberrytech.com
17 West Jefferson Street                    Direct Phone: 301/315-9635
Suite 207                                          Phone: 301/315-9631
Rockville, MD  20850                                 Fax: 301/315-8285
----------------------------------------------------------------------
  Mulberry Technologies: A Consultancy Specializing in SGML and XML
======================================================================

Current Thread