Friday, October 02, 2009

EXPath Packaging System prototype implementation for Saxon

Introduction

After having released a first implementation of EXPath Packaging System for eXist, here is a version for Saxon. You can read this previous blog entry to get more information on the packaging system; in particular, it says: "The concept is quite simple: defining a package format to enable users to install libraries in their processor with just a few clicks, and to enable library authors to provide a single package to be installed on every processors, without the need to document (and maintain) the installation process for each of them."

The package manager for Saxon is a graphical application (a textual front-end will be provided soon,) and is provided as a single JAR file. Go to the implementations page, or use this following direct link to get the JAR. Run it as usual, for instance by double-clicking on it or by executing the command java -jar expath-pkg-saxon-0.1.jar. That will launch the package manager window.

Repositories

The implementation for Saxon differs from the one for eXist in a fundamental way: Saxon does not have a home directory where you can put the installed packaged, and you can invoke Saxon in so many different ways (while the eXist core is always started the same way.) That involves two different aspects regarding package management with Saxon: the package manager itself that installs and remove packages, and a way to configure Saxon itself, regardless with the way you invoke it. In addition, the homeless property of Saxon needs to introduce the concept of package repository.

A repository is a directory dedicated to installing packages, and should only be modified through the package manager. It contains the packages themselves (under a form usable by Saxon) as well as administrative informations to be able to use them (like catalogs, etc.) The graphical package manager allows one to create a new repository directly from the graphical interface, as well as switching between different repositories (if you need to maintain several repositories for several purposes.)

Importing stylesheet

But as I said above, having a repository full of packages is not enough. You have to configure Saxon to use this repository. Because you can invoke Saxon in a plenty of ways, the configuration itself is implemented as a Java helper class that you can use in your own code if you invoke Saxon from within Java (for instance in a Java EE web application.) If you use Saxon from the command line, there is a script that takes care of configuring everything for you.

But before looking in details at how to configure Saxon to use a repository, let's have a look at how a stylesheet can use an installed package. This is the whole point of the packaging system, after all. The goal is simply to be able to use a public import URI in an import statement, this URI being automatically resolved to its local copy in the repository. Like a namespace URI is just a kind of identifier (it is just used as a string, your processor does not try to actually access anything at that address,) the public import URI is an identifier to a specific stylesheet. This machanism supports also having functions implemented in Java. So all you need to do is to use this public URI, like the following:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:h="http://www.example.org/hello"
                version="2.0">

   <xsl:import href="http://www.example.org/hello.xsl"/>

   <xsl:template ...>
      ...
      <xsl:value-of select="h:hello('world')"/>

For XQuery, this is a bit different as XQuery does have a module system. But this is actually very similar. XQuery library modules are identified by their namespace URI. Once again, it can be seen as a public identifier for that XQuery module. So let's say we have an XQuery library module for the namespace URI http://www.example.org/hello, then you can simply write a module that imports it as following:

import module namespace h = "http://www.example.org/hello";
h:hello('world')

And that's it! In the package samples section below, you can see completes examples of such importing stylesheets and queries, as well as the packages they use.

Java configuration

To configure Saxon to use a repository from Java, you need to get a Configuration object. This is a central class in Saxon, which is used almost everywhere in the Saxon code base. You can get it from a Saxon TransformerFactory or from a S9API Processor. With that object on the one hand, and a File object pointing to the repository directory on the other hand, you can just call:

// the repo directory
File          repo   = ...;
// the Saxon config object
Configuration config = ...;
// the EXPath Pkg configurer
ConfigHelper  helper = new ConfigHelper(repo);
// actually configure Saxon
helper.config(config);

Besides the Java code itself, you have to be sure 1/ to have an actual repository at the location you pass to the ConfigHelper constructor and 2/ to have the JAR files used by and containing the extension functions written in Java into your classpath. The only exception to this rule is when you register such an extension function (written in Java) to Saxon 9.2; in this case EXPath Pkg will try to dynamically add the JAR files from the repository to the classpath. But playing with the classpath at runtime is not something I would recommend in Java.

Shell script

When using Saxon from the command line, EXPath Pkg comes with an alternate class to launch Saxon (this class automatically uses ConfigHelper to configure Saxon) as well as with a shell script to launch Saxon with the correct classpath.

To use this shell script (only available on Unix-like systems for now, including Cygwin under Windows) you have to set the environment variables SAXON_HOME to the directory where you put the Saxon JAR files, EXPATH_PKG_JAR to the EXPath Pkg JAR file, and APACHE_XML_RESOLVER_JAR to the XML Resolver JAR file from Apache. Additionally, you can set EXPATH_REPO to the repository directory, to not have to explicitely give it as an option each time you invoke Saxon. If all the above environment variables have been correctly set, and the script added to your PATH, you can just invoke Saxon as usual: saxon -s:source.xml -xsl:stylesheet.xsl.

Use saxon --help to get the usage help of this script. You can set the EXPath repository (and thus override EXPATH_REPO if it is set) with the option --repo=. You can add items to the classpath with the option --add-cp=. You can set the classpath (so overriding SAXON_HOME and other environment variables) with the option --cp=. The script detects if Saxon SA is present, and if so will use the SA version. You can force either B or SA version with either --b or --sa. You can also set any option to the Java Virtual Machine by using --java=, for instance to set a system property, and --mem= to set the amount of memory of the virtual machine (shortcut for the Java option -Xmx) And finally, you can also set the HTTP and HTTPS proxy information with --proxy=host:port (for instance --proxy=proxyhost:8080.)

Package samples

The first example is a packaged version of Priscilla Walmsley's FunctX. This package contains both the XSLT and the XQuery versions of this library. Of course, the XQuery module defines a module namespace, but the XSLT stylesheet does not have any public import URI (as this is behind the standard.) I chose the URI http://www.functx.com/functx-1.0.xsl, but keep in mind this is not official by any means, this is just the URI I chose. It is intended that library authors package their own libraries and choose the public URIs themselves.

The package itself is a plain ZIP file. If you open it or unzip it with your preffered tool, you can see that at the top level, there is a file named expath-pkg.xml. This is the package descriptor, that defines what the package contains (at least what is publicly exported from the package, so what can be used from within a stylesheet or a query.) In the case of this FunctX package, this descriptor looks like:

<package xmlns="http://expath.org/mod/expath-pkg">
   <module version="1.0" name="functx">
      <title>FunctX library for XQuery 1.0 and XSLT 2.0</title>
      <xsl>
         <import-uri>http://www.functx.com/functx-1.0.xsl</import-uri>
         <file>functx-1.0-doc-2007-01.xsl</file>
      </xsl>
      <xquery>
         <namespace>http://www.functx.com</namespace>
         <file>functx-1.0-doc-2007-01.xq</file>
      </xquery>
   </module>
</package>

To install the package, just download it to a temporary location, launch the package manager as explained at the beginning of this blog post, choose "install" in the file menu, and choose the package on your filesystem. To test if it is correctly installed, write the following stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:f="http://www.functx.com"
                version="2.0">

   <xsl:import href="http://www.functx.com/functx-1.0.xsl"/>

   <xsl:template match="/" name="main">
      <result>
         <xsl:sequence select="f:date(1979, 9, 1)"/>
      </result>
   </xsl:template>

</xsl:stylesheet>

and/or the following XQuery main module (depending on what you want to test):

import module namespace f = "http://www.functx.com";

<result> {
   f:date(1979, 9, 1)
}
</result>

To evaluate them, make sure you configured the shell script correctly, as explained above, then open a shell and type one of the following command (or both) where style.xsl is the file where you saved the above stylesheet and query.xq is the file where your saved the above query:

$ saxon -xsl:style.xsl -it:main
<result>1979-09-01</result>
$ saxon --xq query.xq
<result>1979-09-01</result>
$ 

If you prefer to test from Java, just write a simple main class that evaluates the above stylesheet and/or query, taking care of using ConfigHelper to set up the Saxon Configure object. For instance, if you want to use the S9API, you can configure the Processor object like the following (don't forget to add the EXPath Pkg and the Apache XML resolver JAR files to your classpath):

// the repo directory
File         repo   = new File("...");
// the EXPath Pkg configurer
ConfigHelper helper = new ConfigHelper(repo);
// the Saxon processor
Processor    proc   = new Processor(false);
// actually configure Saxon
helper.config(proc.getUnderlyingConfiguration());
// then use 'proc' as usual...

The second sample package provides a single function: ext:hello($who). It is written in Java. Besides other stuff related to the packaging itself, it contains a JAR file with the implementation of that extension function. To test it, just follow the same steps as for the FunctX package, except that you have to add the installed JAR file (from within the repository) to your claspath (this is done automatically for you if you use the shell script, but not if you test it from a Java program.)

Conclusion

This is just a prototype implementation of a package manager for Saxon, which is consistent with the one for eXist. The main issue is the configuration of the classpath, but I think this is best let to the user than having to deal with the classpath, in particular within the context of a Java EE application. This issue shows up also in your IDE configuration. For now, I configure oXygen by adding the catalogs from the repository to the oXygen's main catalog list, and the extension JAR files to the oXygen classpath, so the built-in Saxon processors can be used exactly as usual. But such issues can be resolved by native support right into the processors ad IDEs.

Besides this classpath issue, I am convinced that package management will really improve the current situation, and maybe could be the missing piece to distribute real general-purpose libraries for XQuery and XSLT, and one of the basis to other systems, like an implementation-independent XRX system.

Labels: , , ,

Thursday, June 25, 2009

Divide and Conquer, or XPath, XSLT, XQuery and XProc packaging

Packaging of various X* technologies seems to be of interest for a lot of people for now. And of course it is for me. But it seems everyone comes with its own idea of packaging, as well as a different scope. So to add to the complexity yet, I will present here my own ideas on that matter. Hopefully, I will try to tidy up the different concepts and to identify the different needs. And as always, I like to speak about concrete. To ease further discussions, if only that. So I will introduce a prototype of a packaging system for X* libraries and extensions for Saxon.

Packaging is nothing in itself. It is always related to something else (a language, a technology, a framework...) Packaging is just a mean to ease sharing and delivering something in the scope of that "something else." The several files in an ODF document are packaged in a single ZIP file, with a pre-defined structure, to make it possible for an application to use its content. The important point is not the structure in itself, but rather the information it gathers.

I have followed some very interesting discussions about X* packging during the last few weeks, with very interesting people. Rapidly, I have seen everyone were talking about slightly (or not) different things. The most important point where people have different views IMHO, is the scope of packaging.

As with most of modern languages, an XML developer may have to deliver different pieces of software, depending on the project: libraries, standalone applications, or web applications built for a specific framework. If you look at Java for instance, this is reflected quite clearly in its various packaging formats: JAR files for libraries and applications, WAR files for web applications, EAR files for entire enterprise applications...

WAR files contain Java classes, as JAR files. But the structure is quite different, and there are a few other files, describing what is in the package: "that class is a servlet class, conforming to the definition of servlet and coded to live in a servlet container, with a precise lifecycle," or "the package depends on this JAR file."

The same way, you can package XSLT libraries or XQuery modules, telling a processor that when a stylesheet or a module imports a specific URI, some functions are available (provided as plain XSLT stylesheets, XQuery modules, or extension functions.) Or you can package an entire web application using XProc to control the overall processes, XQuery to query XML databases and XSLT for the presentation layer (sounds very MVC, doesn't it?) But those packages are really different beasts: when the first example just need to package some XSLT, XQuery, Java, whatever code, alonside a simple cataloging system, the second example require to define a complete web framework, its lifecycle, how script can plug into this and exchange information with it ("this XProc pipeline has to be evaluated on an HTTP GET on http://www.example/app/theuri, it knows you will provide it with request information as a wa:http-request element, as we agreed upon, and that XSLT stylesheet has to be applied to its result; by the way it will access runtime information by using the extension functions you provide.")

There has been some work on XRX frameworks, and clearly it would be beneficial for anybody (users, but also implementors,) to have such a standard packaging format for entire applications following their rules (as WAR and EAR files can be to Java.) And they would benefit also from a more low-level packaging format dedicated to package X* libraries, and would build upon them. But they really are at different levels, and I think it is fundamental to make the distinction between both concepts.

As part of the EXPath project, and because I think this is the first step X* technologies need for several years to enable the delivery of libraries, I am particularly interested in a library packaging format.

To illustrate that, I've built a very simple prototype of a package manager for Saxon. On the one hand you have a simple GUI to install and delete packages in a repository, and on the other hand you have a shell script to launch Saxon (setting the classpath for extension functions and setting catalogs to resolve XSLT imports refering to libraries.) If those tools are built around a well-defined, open package format, other implementations could be written (for eXist, for MarkLogic, XQilla, Zorba... but also for oXygen, providing a one-click implementation to install a package and then being able to enable it in some scenarii.)

You can find the manager at http://www.fgeorges.org/purl/20090624/. You should be able to run it simply by clicking on one of the links on the launch.html page (through Java Web Start,) but you can also download the JAR file (look also in the lib/ sub-directory,) putting both JAR files in the classpath and running Java the usual way, with the main class org.expath.pkg.saxon.PackageManagerGUI (there is also a text interface with org.expath.pkg.saxon.PackageManagerTextUI.) You first have to set up an environment variable EXPATH_REPO, pointing to a directory (that will be your EXPath Packaging repository, just create an empty directory.) The interface is very simple: choose the install item in the file menu, and select the package file you want to install. To remove a package, select it in the list of installed modules and select delete in the menu.

Once a module is installed, you can use it via Saxon by adding the additional JARs to the classpath as needed (for extension functions) and by setting up the XML Catalogs support. The following script does that for you: http://www.fgeorges.org/purl/20090624/saxon. It needs a few environment variables: EXPATH_REPO as explained above, APACHE_XML_RESOLVER_JAR must point to the Apache XML Commons Resolver (see http://xml.apache.org/commons/, and be sure to pick the resolver JAR) and SAXON_HOME must point to the directory containing the Saxon JARs.

But what about the package format itself? In this prototype, this is a simple ZIP file, with the following structure:

expath-pkg.xml
expath-http-client/
   saxon/
      xsl/
         expath-http-client-saxon.xsl
      jar/
         expath-http-client-saxon.jar
      lib/
         commons-codec-1.3.jar
         ...jar

where expath-pkg.xml is the package descriptor, and expath-http-client is the directory containing one module (here the EXPath HTTP Client module.) This module is implemented as a Java extension, besides a frontend XSLT stylesheet that take care of Saxon-specifics to bind to the Java functions. During the install, an XML Catalogs file is created, to resolve the URI http://www.expath.org/mod/http-client.xsl to that stylesheet, in the local repository. One stylesheet can then simply import that URI and use the functions of the module. The real package for the HTTP Client can be downloaded at the same place: http://www.fgeorges.org/purl/20090624/expath-http-client-saxon-0.3.zip.

There are of course still a lot of work defining exactly the package format, how to handle dependencies, improving the implementation... But I think that gives the big picture. If you are interested, here is what the package descriptor looks like:

<package xmlns="http://expath.org/mod/expath-pkg">
   <module version="0.3" name="expath-http-client">
      <title>EXPath HTTP Client</title>
      <xsl>
         <import-uri>http://www.expath.org/mod/http-client.xsl</import-uri>
         <file>saxon/xsl/expath-http-client-saxon.xsl</file>
      </xsl>
   </module>
</package>

We can see the package contains one module, namely "EXPath HTTP Client," version 0.3. The URIs are used to create an XML catalog. This version of the package contains all the dependencies (the JARs used by the Java implementation of the extension functions,) but they can be also left out, and configured with the following element:

<saxon>
   <dep type="jar">
      <title>Apache Commons Codec 1.3</title>
      <home>http://jakarta.apache.org/commons/codec/</home>
   </dep>
   <dep type="jar">
      <title>Apache Commons Logging 1.1.1</title>
      <home>http://commons.apache.org/logging/</home>
   </dep>
   <dep type="jar">
      <title>Apache HTTP Client 4.0-beta2</title>
      <home>http://hc.apache.org/</home>
   </dep>
   <dep type="jar">
      <title>Apache HTTP Core 4.0</title>
      <home>http://hc.apache.org/</home>
   </dep>
   <dep type="jar">
      <title>Tagsoup 1.2</title>
      <home>http://home.ccil.org/~cowan/XML/tagsoup/</home>
      <href>http://home.ccil.org/~cowan/XML/tagsoup/tagsoup-1.2.jar</href>
   </dep>
</saxon>

The GUI does not take them into account yet, but it should propose to automatically download JARs when possible, and give the user a list of libraries and their homepage when a manual download is required. But of course, the same format can be used to package standard XSLT stylesheets, without any Java features, just by mapping the main entry point files to their public URIs.

Of course, this format will be particularly useful once precisely defined in an open spec, and if several processors support it (either natively, or through external managers.)

To end this post, I would like to introduce an idea from Jim Fuller: CXAN. I am sure most of you know CTAN for TeX, or CPAN for Perl. They are central, organized repositories of libraries for those languages, accessible throught HTTP. With a proper packaging format, it would be possible to set up such a web repository gathering XPath, XSLT, XQuery and XProc libraries and applications, installable automatically with a manager that would install a package from its name, handling dependencies and the like. But for sure, that is yet a step forward.

Labels: , , ,

Friday, February 27, 2009

XSLStyle and oXygen

On almost every XSLT projects I worked on, I used Ken Holman's XSLStyleTM. It enables one to document each stylesheet component (template, function, variable, module, etc.) using an XML vocabulary (DocBook and DITA are supported out of the box.) For instance, the following exerpt shows how to document a simple named template with a parameter, assuming the vocabulary has been set to DocBook:

<doc:template>
   <para>Create a paragraph with a greetings message.</para>
   <doc:param name="who">
      <para>The name of the person to address the greetings to.</para>
   </doc:param>
</doc:template>
<xsl:template name="greetings">
   <xsl:param name="who" as="xs:string"/>
   <h:p>
      <xsl:value-of select="concat('Hello, ', $who, '!')"/>
   </h:p>
</xsl:template>

XSLStyleTM is a set of stylesheets to extract this information and format it to an HTML page. Besides this formating tasks, it also checks for best-practices (did you declare the type for parameters?, etc.) I think this is a very important piece in the XSLT writer's toolbox.

Seting a project up to use XSLStyle is as easy as adding a tranform task using the XSLStyle's stylesheets to transform the project's stylesheet to HTML pages. And of course to add documentation to the stylesheets. All that is very easy, but you always have to remember where to installed XSLStyle, check for the namespace URIs to use, and the exact element names to use to document your code. Once again, that's very easy, but repetitive and time-consuming.

I use oXygen more and more for a few months. And I really enjoy it. It helps a lot automating repetitive, time-consuming tasks: create a new stylesheet with the right namespace URIs, code completion for known vocabularies, tansform scenarii, etc. And for schemas, XQuery modules and WSDL definitions, it provides actions to generate documentation, the same way XSLStyle generates documentation for XSLT modules. Unfortunately, it does not support XSLStyle yet. Actually, the scenario attached to almost all of my stylesheets is the scenario using XSLStyle.

But having XSLStyle integrated within oXygen would bring several advantages. It would always be installed alongside with oXygen. I am a freelance consultant and move a lot between different companies, using several computers, with different system administration policies. Thanks to the per-person-license model of oXygen, and its platform independence, I can take it with me everywhere, and be quickly productive, without having to install separately a JRE, Saxon, FOP, Xerces, DocBook environment, RELAX NG tools, Schematron skeletons, Emacs with nXML and others modes, and even Cygwin to be able to create shell scripts and Makefile to automate tasks. But I still have to download XSLStyle separately, open one of its stylesheets to copy and paste a documentation sample and the correct namespace bindings. Of course, it would be time saving to have an XSLStyle framework directly in oXygen.

Besides that point, oXygen could offer editing facilities for stylesheet components documentation as well. When you want to document a named template with several parameters, you have to create the documentation structure as showed above, and create a doc:param element for each parameter, with the correct name. This could be automatically done with a specific action, to add an empty documentation structure to a particular component or even to each non-documented yet componnents in the stylesheet:

There would not have anymore the need to attach the stylesheet to an XSLStyle scenario neither. When I work on a stylesheet, I like to have its current scenario bound regarding what I am working on in particular. Switching back and forth between this scenario and the XSLStyle scenario does not really make sense. It would be more productive to have a Tools > Generate Documentation > XSLT Documentation... action as for the other file type, in my humble opinion.

And finally, oXygen would even be able to report some documentation errors directly in the editor pane, as wrong names in documenting parameters.

I hope oXygen team will be intersted to add support for XSLStyle, as this would be convenient, but also would spread XSLT writing best practices advocated by XSLStyle. Anyway, thanks to the team for listening to user requests, this is too rarely the case in other companies, and of course to Ken for XSLStyle.

Labels: ,

Tuesday, December 09, 2008

FXSL currying and nestable sequences

After an interesting discussion on the FXSL Help forum, the problem of currying and nested sequences showed up again. The FXSL project provides, among other things, first-class citizen functions. Basically, it represents a function as an element. When executing such a function, the dispatching to the code is done by applying templates on that element.

An interesting feature of FXSL is the ability to curry parameters to a function, to create an other function of a lesser order. The principle is to attach parameters to the function. This new function can then be used as any other function, with specified parameters bound to specified values.

The result of currying is then another first-class citizen function. So it has to be a node, because f:apply() applies templates on it to find the code to execute. And it has to be a single item, in order to be used as any other items (in particular its behaviour in sequence handling and atomization.) The later point makes it impossible to use a sequence as result of currying.

The approach taken by FXSL for now is to create an XML element as the result of f:curry(). This element contains several information: the child fun holds the curried function (may be itself a currying), cnArgs is the cardinality of the curried function and then the childs arg hold the curried values. For instance, the expression f:curry(my:add(), 2, 1024) will return the following element:

<f-curry:f-curry xmlns:f-curry="http://fxsl.sf.net/curry">
   <fun>
      <my:add xmlns:my="urn:X-FGeorges.org:tests:curry-sref.xsl"/>
   </fun>
   <cnArgs>2</cnArgs>
   <arg t="xs:integer">1024</arg>
</f-curry:f-curry>

This approach is convenient because we can use any structure we need to represent currying. Unfortunately, the semantics of adding items to an XML tree implies to copy nodes and to make nodes from atomic values. That means that if the curried argument is an XML element, piece of a whole document, it will be copied to the element representing currying. For example if the curried function uses the ancestor axis on this curried element, it will see the f-curry:f-curry element, instead of the ancestors in the original document. That was actually the problem reported by Christoph Lange on the FXSL forum.

And this leads to other problems related to identity. For instance, items are transformed to nodes. FXSL resolves that problem by recording the initial type in the currying structure, and convert the node back to that type. While this is ok for standard simple types, that can't be applied to user-defined simple types. Another example is for validated nodes; they loose their type annotations when added to the currying structure, which can be a problem for the curried function. You can find more about this topic in Type-preserving copy in XSLT 2.0.

Actually, all those problem could be solved with a simple feature that does not exist in standard XPath: the ability to nest sequence. If we could nest sequences, or if we had a special type of sequences that wouldn't atomize when added to another sequence, we could use them as the result of currying. Even if that's not a node anymore, we could adapt f:apply() to handle those particular sequences and use its, say, first item as the node to apply templates on.

The good news is that this is simple to implement such a sequence as a Java extension in Saxon. Here is a very simple implementation. I have called it SRef, for Sequence Reference. I guess we would need something more elaborated to be efficient and general-purpose, but this is just a proof-of-concept:

package org.fgeorges.saxon;

import java.util.ArrayList;
import java.util.List;
import net.sf.saxon.om.ArrayIterator;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.trans.XPathException;

/**
 * XPath sequence reference, or non-atomizable XPath sequence.
 *
 * @author Florent Georges - fgeorges.org
 * @date 2006-12-01
 */
public class SequenceRef
{
    public SequenceRef(SequenceIterator seq) throws XPathException
    {
        myIter = seq.getAnother();
    }

    public SequenceIterator getSequence()
    {
        return myIter;
    }

    static public boolean isSequenceRef(Object obj)
    {
        return obj instanceof SequenceRef;
    }

    @Override
    public String toString()
    {
        throw new RuntimeException("toString not supported, cannot be added to a tree!");
    }

    private SequenceIterator myIter = null;
}

This implementation in Java is coupled to an simple API in XPath. Three functions are created: sref:make-sref() takes a sequence and returns an sref for this sequence, sref:sequence() takes an sref and return the original sequence, and sref:is-sref() get an item and return true if it is an sref. The following XSLT module defines those functions:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:sref="http://www.fgeorges.org/xslt/sref"
                xmlns:impl="java:org.fgeorges.saxon.SequenceRef"
                exclude-result-prefixes="xs sref impl"
                version="2.0">

   <xsl:function name="sref:make-sref" as="item()">
      <xsl:param name="seq" as="item()*"/>
      <xsl:sequence select="impl:new($seq)"/>
   </xsl:function>

   <xsl:function name="sref:sequence" as="item()*">
      <xsl:param name="ref" as="item()"/>
      <xsl:sequence select="impl:getSequence($ref)"/>
   </xsl:function>

   <xsl:function name="sref:is-sref" as="xs:boolean">
      <xsl:param name="ref" as="item()"/>
      <xsl:sequence select="impl:isSequenceRef($ref)"/>
   </xsl:function>

   <xsl:function name="sref:atomize" as="item()*">
      <xsl:param name="seq" as="item()*"/>
      <xsl:sequence select="
          for $item in $seq return
            if ( sref:is-sref($item) ) then
              sref:sequence($item)
            else
              $item"/>
   </xsl:function>

   <xsl:function name="sref:deep-atomize" as="item()*">
      <xsl:param name="seq" as="item()*"/>
      <xsl:sequence select="
          for $item in $seq return
            if ( sref:is-sref($item) ) then
              sref:deep-atomize(sref:sequence($item))
            else
              $item"/>
   </xsl:function>

</xsl:stylesheet>

With those simple functions, it is then possible to modify f:curry() and f:apply() to support (to take advantage of) SRefs. The folowing is a simple example (supporting only currying a function of cardinality 2 with a single argument). I create a first-citizen function my:add() that takes two integers and returns their sum, I write new versions of f:apply() and f:curry(), then I call my:add() both directly and with currying:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:f="http://fxsl.sf.net/"
                xmlns:my="urn:X-FGeorges.org:tests:curry-sref.xsl"
                xmlns:sref="http://www.fgeorges.org/xslt/sref"
                xmlns:impl="java:org.fgeorges.saxon.SequenceRef"
                exclude-result-prefixes="xs f my sref impl"
                version="2.0">

   <xsl:import href="sref.xsl"/>

   <xsl:output indent="yes"/>

   <!--
      The my:add() first class function.
   -->
   <xsl:variable name="my:add" as="element()">
      <my:add/>
   </xsl:variable>

   <xsl:function name="my:add" as="node()">
      <xsl:sequence select="$my:add"/>
   </xsl:function>

   <xsl:function name="my:add" as="xs:integer">
      <xsl:param name="lhs" as="xs:integer"/>
      <xsl:param name="rhs" as="xs:integer"/>
      <xsl:sequence select="$lhs + $rhs"/>
   </xsl:function>

   <xsl:template match="my:add" mode="f:FXSL">
      <xsl:param name="arg1"/>
      <xsl:param name="arg2"/>
      <xsl:sequence select="my:add($arg1, $arg2)"/>
   </xsl:template>

   <!--
      Apply on SRefs.
   -->
   <xsl:function name="f:apply-sref">
      <xsl:param name="pFunc" as="item()"/>
      <xsl:param name="arg1" as="item()*"/>
      <xsl:variable name="seq" select="sref:sequence($pFunc)"/>
      <xsl:apply-templates select="$seq[1]" mode="f:FXSL">
         <xsl:with-param name="seq" select="$seq"/>
         <xsl:with-param name="arg1" select="$arg1"/>
      </xsl:apply-templates>
   </xsl:function>

   <!--
      Currying using SRefs.
   -->
   <xsl:function name="f:curry-sref" xmlns:f-c-s="http://fxsl.sf.net/curry-sref">
      <xsl:param name="pFun" as="node()"/>
      <xsl:param name="pNargs" as="xs:integer"/>
      <xsl:param name="arg1"/>
      <xsl:variable name="curry-fun" as="element()">
         <f-c-s:f-c-s/>
      </xsl:variable>
      <xsl:sequence select="
          sref:make-sref(($curry-fun, $pFun, $pNargs, sref:make-sref($arg1)))"/>
   </xsl:function>

   <xsl:template match="f-c-s:*" mode="f:FXSL"
       xmlns:f-c-s="http://fxsl.sf.net/curry-sref">
      <xsl:param name="seq" as="item()*"/>
      <xsl:param name="arg1" as="item()*"/>
      <xsl:apply-templates select="$seq[2]" mode="f:FXSL">
         <xsl:with-param name="arg1" select="sref:sequence($seq[position() gt 3])"/>
         <xsl:with-param name="arg2" select="$arg1"/>
      </xsl:apply-templates>      
   </xsl:template>

   <!--
      The testing template.
   -->
   <xsl:template match="/">
      <root>
         <test-1>
            <xsl:sequence select="my:add(512, 1024)"/>
         </test-1>
         <test-2>
            <xsl:variable name="fun" select="f:curry-sref(my:add(), 2, 1024)"/>
            <xsl:sequence select="f:apply-sref($fun, 512)"/>
         </test-2>
      </root>
   </xsl:template>

</xsl:stylesheet>

Thanks to Christoph Lange for the original problem and to Dimitre for his ideas.

Labels: , ,