Recent releases of Saxon go to some lengths to avoid moving a call to an extension function out of a loop. I'm afraid I can't tell you if the same was true of Saxon 8.6.1 - it was released some time ago. Using saxon:explain="yes" would tell you whether your conjecture is correct that the function call has been executed outside the loop.
 
Try this one on 9.0 to see if the behaviour is still the same.
 
Having said that, there will always be a certain amount of unpredictability concerning functions with side-effects. Although some optimizations try to treat them specially, others (notably lazy evaluation) don't. The XSLT specification offers you no guarantees, and as far as Saxon is concerned, it's really a case of "if it works, then you can use it, but if it doesn't, then you can't".
 
A better design would be to call a function that takes position() as an argument. Even if the function still has side-effects internally, this more-or-less forces Saxon to call it once to obtain each value.
 
Michael Kay
http://www.saxonica.com/


From: saxon-help-bounces@lists.sourceforge.net [mailto:saxon-help-bounces@lists.sourceforge.net] On Behalf Of Hedenus, Michael (EXT)
Sent: 01 February 2008 08:26
To: saxon-help@lists.sourceforge.net
Subject: [saxon] Does Saxon assume that extension functions with noarguments have no side-effects?

Dear All,
 
again using Saxon 8.6.1 (I stick to it due to my project) I found a behavior of Saxon of which is not clear if it is a bug or feature.
 
Consider following stylesheet:
 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">       
 <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
 <xsl:variable name="vars">
  <a>a1</a>
  <a>a2</a>
  <a>a3</a>
 </xsl:variable>
 <xsl:template match="/">
  <!-- (1) -->
  <xsl:for-each select="$vars/a" xmlns:Ex="java:test.Extensions">
   <b id="{Ex:createId()}">
    <xsl:value-of select="."/>
   </b>
  </xsl:for-each>
  <!-- (2) -->
  <xsl:for-each select="$vars/a" xmlns:Ex="java:test.Extensions">
   <c id="{Ex:createId()}_{position()}">
    <xsl:value-of select="."/>
   </c>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>
 
Here an extension function createId() with no arguments is used wihtin a loop. The function shall create values like 'ID_<nr>' similar to generate-id() but the function has a side-effect that increases an internal counter:
 
package test;
 
public class Extensions
{
 private static long counter;
 
 public static String createId()
 {
  return "ID_" + (++counter);
 }
 
If I run the stylesheet using itself as input the result is:
 
D:\work\misc\saxon8>java -cp .;h:\saxon8\saxon8.jar -Xmx512M net.sf.saxon.Transform test.xsl test.xsl
<?xml version="1.0" encoding="UTF-8"?>
<b xmlns:Ex="java:test.Extensions" id="ID_1">a1</b>
<b xmlns:Ex="java:test.Extensions" id="ID_1">a2</b>
<b xmlns:Ex="java:test.Extensions" id="ID_1">a3</b>
<c xmlns:Ex="java:test.Extensions" id="ID_2_1">a1</c>
<c xmlns:Ex="java:test.Extensions" id="ID_3_2">a2</c>
<c xmlns:Ex="java:test.Extensions" id="ID_4_3">a3</c>
 
In the first loop,  the extension function createId() is called only once (three times 'ID_1')!
In the second loop,  the extension function createId() is invoked for each loop.
 
Does Saxon do some optimization in case (1) and extracts the function call of Ex:createId() because it is considered to be constant?
In case (2) such kind of optimization is impeded by position().
 
If true, could one ever use random() in XSLT ?
Is there anything said in the XSLT / XPath specs about how extension functions are considered to behave?
 
Mit den besten Gruessen/Best regards,

Dr. Michael Hedenus

VDO Automotive AG
SV P EG 1
Siemensstrasse 12, 93055 Regensburg, Germany

Ein Unternehmen des Continental-Konzerns/A Company of the Continental Corporation

Telefon/Phone: +49 941 790-6362
Telefax: +49 941 79013-6362
E-Mail: Michael.Hedenus-EXT@continental-corporation.com
http://www.continental-corporation.com
__________________________________________

VDO Automotive AG, Siemensstr. 12, 93055 Regensburg
Postfach/Postbox 10 09 43, 93009 Regensburg
Vorsitzender des Aufsichtsrats/Chairman of the Supervisory Board: Johannes Suttmeyer
Vorstand/Managing Board: Dr. Alan Hippe, Helmut Matschi
Sitz der Gesellschaft/Registered office: Regensburg
Registergericht/Commercial registry: Regensburg, HRB 10510

__________________________________________

Proprietary and confidential. Distribution only by express authority of Continental AG or its subsidiaries.