1. | Dynamic Functions article. | ||||||||||
"Dynamic Functions using FXSL: Composition, Partial Applications and Lambda Expressions" at topxml.com This one is at least as important (if not more) than the first article on functional programming in XSLT. I think it will be most useful for people, who are interested in further understanding and using FP in XSLT. Contents:
two excerpts: "This article is a follow-up from the recent publication "The Functional Programming Language XSLT". Provided here is the answer to a FAQ about functional programming in XSLT: Can functions be created dynamically during run-time and what are some general ways to achieve this?. Using the XSLT functional programming library FXSL, examples are given of performing functional composition, currying and partial application of functions, as well as of dynamically creating a function, which corresponds to a lambda expression." "This article demonstrated and explained the details of an XSLT implementation of three major ways of dynamically creating functions and using them:
The XSLT implementation of partial application is more powerful than the Haskell one, because it allows binding of arguments regardless of order. At the same time partial application in XSLT is very convenient, using the traditional XSLT way of specifying arguments through xsl:with-param. Currying and partial application support has been included in the latest release of the functional programming library FXSL. Also demonstrated was a general way to model and implement in XSLT objects having inheritance and virtual functions." | |||||||||||
2. | Generic templates | ||||||||||
If a template must be instantiated (based on the value of $someVar) using xsl:choose, this means that the names/modes of all such templates must be known in advance. Every time we add a new template to be called, the calling template's xsl:choose must be updated to incorporate the new case. Obviously this static invocation scheme is hardly convenient. Actually there is a method for building generic templates so that they can cause different templates to be istantiated dynamically and without using xsl:choose. It is not necessary to know anything in advance about the templates to be istantiated, and in fact they may be written long after the generic template. The idea is to pass to the generic template a separate parameter, which uniquely and ***dynamically*** identifies the template that will be instantiated. This parameter is of type node-set (single node), that has a unique namespace-uri. The caller of the generic template may define many different "action templates" that match only specific unique types of nodes. During the execution of a stylesheet a generic template may be called many times with different "action template types" to perform similar processing. Below is an example of a generic template that finds and returns the "maximum" node in a node-set. The meaning of "maximum" is determined completely by an external template, which implements the relation "greater". A "template type" parameter is passed to the generic template, so that it can dynamically invoke the necessary "greater"-action template. In this example there are two "greater"-action templates implemented -- one that uses the traditional ">" relation for numbers, and another that compares their reciprocal values. There could be potentially unlimited number of different "greater"-action templates - -- e.g. comparing distance, space, perimeter,... etc. They can all be passed to a single generic "maximum" template and it will always find the correct maximum node. The same method can be used for a generic sort template and in the implementation of many other useful generic algorithms. This method does't have the problem of "the last overrides override all" (described a few months ago by Jeni) when there are several templates overriding some templates of an imported stylesheet. Genericity is an extremely useful feature -- at present it is missing from both the EXSLT site and the Standard XSLT Library site. In my opinion in order for a standard template library to be really useful, it must contain ***generic*** templates. Thus it will have an unlimited number of applicationsas opposed to a fixed, very small number of applications. In the near future I will provide some initial set of useful generic templates -- min, max, sort, partial sort, binary search,... etc. Here's the example: XML source document: <numbers> <num>1</num> <num>3</num> <num>2</num> <num>9</num> <num>4</num> <num>6</num> <num>5</num> <num>7</num> <num>8</num> </numbers> Stylesheet: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:num="num" xmlns:num2="num2" > <num:node/> <num2:node/> <xsl:output omit-xml-declaration="yes" /> <xsl:variable name="gtNum-node" select="document('')//num:*"/> <xsl:variable name="gtNum2-node" select="document('')//num2:*"/> <xsl:template match="/"> <xsl:call-template name="get-max"> <xsl:with-param name="greaterSelector" select="$gtNum-node" /> <xsl:with-param name="nodes" select="/numbers/num" /> </xsl:call-template> <xsl:text>
</xsl:text> <xsl:call-template name="get-max"> <xsl:with-param name="greaterSelector" select="$gtNum2-node" /> <xsl:with-param name="nodes" select="/numbers/num" /> </xsl:call-template> </xsl:template> <xsl:template name="get-max"> <xsl:param name="greaterSelector" select="/*"/> <xsl:param name="nodes" /> <xsl:choose> <xsl:when test="$nodes"> <xsl:variable name="max-of-rest"> <xsl:call-template name="get-max"> <xsl:with-param name="greaterSelector" select="$greaterSelector" /> <xsl:with-param name="nodes" select="$nodes[position()!=1]" /> </xsl:call-template> </xsl:variable> <xsl:variable name="isGreater"> <xsl:apply-templates select="$greaterSelector" > <xsl:with-param name="n1" select="$nodes[1]"/> <xsl:with-param name="n2" select="$max-of-rest"/> </xsl:apply-templates> </xsl:variable> <xsl:choose> <xsl:when test="$isGreater = 'true'"> <xsl:value-of select="$nodes[1]" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="$max-of-rest" /> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <xsl:value-of select="-999999999" /> <!-- minus infinity --> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="isGreaterNum" match="node()[namespace-uri()='num']"> <xsl:param name="n1"/> <xsl:param name="n2"/> <xsl:value-of select="$n1 > $n2"/> </xsl:template> <xsl:template name="isGreaterNum2" match="node()[namespace-uri()='num2']"> <xsl:param name="n1"/> <xsl:param name="n2"/> <xsl:value-of select="1 div $n1 > 1 div $n2"/> </xsl:template> </xsl:stylesheet> The result produced is the two correct maximums: 9 1 | |||||||||||
3. | dynamically calling templates | ||||||||||
I have submitted to the EXSL list a number of generic templates implementing generic functions as specified in the subject. One of the neat ideas in these implementations is a possible answer to the frequently asked question "how do I call templates dynamically?" - how to choose the name of the template that you want to call based on a variable. One answer is to add a match pattern to the template that matches a (single) node that acts as a proxy or UID for the template. Dimitre's examples use a specially-defined node at the top level of the stylesheet. For named templates, another approach would be to use the xsl:template elements themselves, e.g.: <xsl:template name="my:named-template" match="xsl:template[@name = 'my:namedTemplate']"> ... </xsl:template> You can then apply templates to the relevant xsl:template element to have the same kind of effect as calling the template. I would probably set up a global variable to hold the template elements: <xsl:variable name="templates" select="document('')/xsl:template" /> [Note: this only gets the templates in the current physical stylesheet, not any that have been imported/included, which could be a big drawback - is this something that could be addressed with the closure() function that Christian Nentwich suggested?] And then filter that to get the relevant one: <xsl:apply-templates select="$templates[@name = $template-name]" /> [Note: you could use the template elements as nodes with Dimitre's generic templates, but they'd be passed as a parameter rather than have templates applied to them.] The big difference between applying templates in this way and calling the template is that the current node and position() within the template body will be the xsl:template element and 1 respectively. You can get around this problem by passing in parameters holding these values in the context in which the template is called/applied: <xsl:apply-templates select="$templates[@name = $template-name]"> <xsl:with-param name="current" select="." /> <xsl:with-param name="position" select="position()" /> </xsl:apply-templates> I'm not sure if this is new, but it's not something that we've been giving as an answer to the 'dynamically calling templates' FAQ, so I thought it would be worth describing. |