ASPN ActiveState Programmer Network
ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups


Recent Messages
List Archives
About the List
List Leaders
Subscription Options

View Subscriptions
Help

View by Topic
ActiveState
.NET Framework
Open Source
Perl
PHP
Python
Tcl
Web Services
XML & XSLT

View by Category
Database
General
SOAP
System Administration
Tools
User Interfaces
Web Programming
XML Programming


MyASPN >> Mail Archive >> xsl-list
xsl-list
[xsl] Re: higher-order functions in xslt
by Dimitre Novatchev other posts by this author
Nov 25 2001 8:13PM messages near this date
Re: [xsl] Netscape changes font-size in table | [xsl] generate xml from excel file
>  Being impressed with Dimitre's generic templates I sunk a little in a functional
>  programming and here is my q: Is there a way to implement higher order functions 
>  in xslt (I mean generating new functions, returning them as a result and applying
>  them)?
>  
>  ---
>  Oleg Tkachenko,
>  Multiconn International, Israel 

Hi Oleg,

Yes, using generic templates we can generate new functions and return them, so that
they can be applied later.

Here's an example, taken right from the book of Simon Thompson "Haskell: The Craft
of Functional Programming".

Suppose we are to build a calculator for numerical expressions. As part of our
system we need to be able to model the current values of the user-defined variables,
which we might call ***the store*** of the calculator.

To model the store, we define an abstract data type (ADT), which has the following
signature:

initial :: Store
value   :: Store ->  Var -> Int
update  :: Store ->  Var -> Int -> Store

The function "initial" produces an initial (empty) store.
The function "value" retrieves from the store the value (Int) of a variable name.
The function "update" updates the store with a new (variable-name, value)
association.

This ADT can have different implementations, e.g. by keeping an internal list of
couples (varName, value), or by implementing a function that given a variable name
returns its value.

The latter implementation is defined in Haskell in the following way:

newtype Store = Sto (Var ->  Int)

initial :: Store
initial =  Sto (\v ->  0)

value   :: Store ->  Var -> Int
value (Sto sto) v = sto v

update  :: Store ->  Var -> Int -> Store
update (Sto sto) v n 
  = Sto (\w ->  if v == w then n else sto w) 

Under this implementation:
  - the initial store maps every variable to 0.
  - to look up a value of a variable v the store function sto is applied to v
  - in the case of an update, a function returned is identical to sto except on the
variable, whose value is changed.

Here's the corresponding XSLT implementation:

store.xsl
---------
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:getInitialStore="f:getInitialStore"
xmlns:getValue="f:getValue0"
xmlns:upd-getValue="f:upd-getValue"
xmlns:updateStore="f:updateStore"
> 
  
  <xsl:template name="getInitialStore" match="getInitialStore:*"> 
    <store> 
      <getInitialStore:getInitialStore/> 
      <getValue:getValue/> 
      <updateStore:updateStore/> 
    </store> 
  </xsl:template> 
  
  <xsl:template match="getValue:*"> 
     <xsl:value-of select="0"/> 
  </xsl:template> 
  
  <xsl:template match="updateStore:*"> 
    <xsl:param name="pName"/> 
    <xsl:param name="pValue"/> 
    
    <store> 
      <getInitialStore:getInitialStore/> 
      <upd-getValue:getValue> 
        <store> <xsl:copy-of select="../*"/></store>
        <name> <xsl:value-of select="$pName"/></name>
        <value> <xsl:value-of select="$pValue"/></value>
      </upd-getValue:getValue>  
      <updateStore:updateStore/> 
    </store> 
  </xsl:template> 
  
  <xsl:template match="upd-getValue:*"> 
    <xsl:param name="pName"/> 
    
    <xsl:choose> 
      <xsl:when test="$pName = name"> 
        <xsl:value-of select="value"/> 
      </xsl:when> 
      <xsl:otherwise> 
        <xsl:apply-templates select="store/*[local-name()='getValue']"> 
           <xsl:with-param name="pName" select="$pName"/> 
        </xsl:apply-templates> 
      </xsl:otherwise> 
    </xsl:choose> 
  </xsl:template> 
</xsl:stylesheet> 

Let's test it.

testStore.xsl
-------------
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
> 

  <xsl:import href="store.xsl"/> 
  
  <xsl:output method="text"/>   

  <xsl:template match="/">  
    
    <!-- Get Initial Store --> 
    <xsl:variable name="vrtfStore0"> 
      <xsl:call-template name="getInitialStore"/> 
    </xsl:variable> 
    
    <xsl:variable name="vStore0" select="msxsl:node-set($vrtfStore0)/*"/> 
    
    <!-- Get A, B, C from the initial store: must be 0-s --> 
      vStore0:
      A='<xsl:apply-templates select="$vStore0/*[local-name() = 'getValue']"> 
          <xsl:with-param name="pName" select="'A'"/> 
         </xsl:apply-templates> '
      B='<xsl:apply-templates select="$vStore0/*[local-name() = 'getValue']"> 
          <xsl:with-param name="pName" select="'B'"/> 
         </xsl:apply-templates> '
      C='<xsl:apply-templates select="$vStore0/*[local-name() = 'getValue']"> 
          <xsl:with-param name="pName" select="'C'"/> 
         </xsl:apply-templates> '

    <!-- Update store0 with 'A=1' --> 
    <xsl:variable name="vrtfStore1"> 
      <xsl:apply-templates select="$vStore0/*[local-name() = 'updateStore']"> 
        <xsl:with-param name="pName" select="'A'"/> 
        <xsl:with-param name="pValue" select="1"/> 
      </xsl:apply-templates> 
    </xsl:variable> 

    <xsl:variable name="vStore1" select="msxsl:node-set($vrtfStore1)/*"/> 
    <!-- Get A, B, C from the store1: A is 1, the rest must be 0-s --> 
      vStore1:
      A='<xsl:apply-templates select="$vStore1/*[local-name() = 'getValue']"> 
          <xsl:with-param name="pName" select="'A'"/> 
         </xsl:apply-templates> '
      B='<xsl:apply-templates select="$vStore1/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'B'"/> 
         </xsl:apply-templates> '
      C='<xsl:apply-templates select="$vStore1/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'C'"/> 
         </xsl:apply-templates> '
    
    <!-- Update store1 with 'B=2' --> 
    <xsl:variable name="vrtfStore2"> 
      <xsl:apply-templates select="$vStore1/*[local-name() = 'updateStore']"> 
        <xsl:with-param name="pName" select="'B'"/> 
        <xsl:with-param name="pValue" select="2"/> 
      </xsl:apply-templates> 
    </xsl:variable> 

    <xsl:variable name="vStore2" select="msxsl:node-set($vrtfStore2)/*"/> 
    <!-- Get A, B, C from the store2: A is 1, B is 2, the rest must be 0-s --> 
      vStore2:
      A='<xsl:apply-templates select="$vStore2/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'A'"/> 
         </xsl:apply-templates> '
      B='<xsl:apply-templates select="$vStore2/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'B'"/> 
         </xsl:apply-templates> '
      C='<xsl:apply-templates select="$vStore2/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'C'"/> 
         </xsl:apply-templates> '
    
    <!-- Update store2 with 'C=3' --> 
    <xsl:variable name="vrtfStore3"> 
      <xsl:apply-templates select="$vStore2/*[local-name() = 'updateStore']"> 
        <xsl:with-param name="pName" select="'C'"/> 
        <xsl:with-param name="pValue" select="3"/> 
      </xsl:apply-templates> 
    </xsl:variable> 

    <xsl:variable name="vStore3" select="msxsl:node-set($vrtfStore3)/*"/> 
    <!-- Get A, B, C from the store3: A is 1, B is 2, C is 3,the rest must be 0-s
--> 
      vStore3:
      A='<xsl:apply-templates select="$vStore3/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'A'"/> 
         </xsl:apply-templates> '
      B='<xsl:apply-templates select="$vStore3/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'B'"/> 
         </xsl:apply-templates> '
      C='<xsl:apply-templates select="$vStore3/*[local-name() = 'getValue']"> 
           <xsl:with-param name="pName" select="'C'"/> 
         </xsl:apply-templates> '
   </xsl:template> 
</xsl:stylesheet> 

When applied on any xml source, the above transformation produces the following
result:


      vStore0:
      A='0'
      B='0'
      C='0'

    
      vStore1:
      A='1'
      B='0'
      C='0'
    
    
      vStore2:
      A='1'
      B='2'
      C='0'
    
    
      vStore3:
      A='1'
      B='2'
      C='3'
   
What is important to note in this example is that on every call of the updateStore
function, it creates the template reference for a new getValue function, like this:

      <upd-getValue:getValue> 
        <store> <xsl:copy-of select="../*"/></store>
        <name> <xsl:value-of select="$pName"/></name>
        <value> <xsl:value-of select="$pValue"/></value>
      </upd-getValue:getValue>  

This template reference always initiates the same template rule, ***but for a
different function***.

This function may call a chain of previously defined getValue functions for the
previos stores, until the first occurence of a variable name is found, or if not,
then finally the initial getValue function instantiates another template rule to
return the value 0.

There are two important lessons from this example:

 1. We demonstrated the dynamic creation of new functions, and their later
application to produce the necessary results.

 2. A function is not just "a template", but a template + context. The same template
may implement different functions, given different context.

Hope this helped.

Cheers,
Dimitre Novatchev.


















__________________________________________________
Do You Yahoo!?
Yahoo! GeoCities - quick and easy web site hosting, just $8.95/month.
http://geocities.yahoo.com/ps/info1

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list

Privacy Policy | Email Opt-out | Feedback | Syndication
© ActiveState Software Inc. All rights reserved