xsl:accumulator

The xsl:accumulator declaration is new in XSLT 3.0, and is fully implemented in Saxon 9.5. Saxon-PE supports a non-streaming implementation; Saxon-EE additionally supports a streaming implementation.

Accumulators are motivated by the need for streaming, but they can also be useful independently of streaming. The rationale is that when you are processing a document using streaming, you only get to see each node once, so it's quite likely that you need to remember what you have seen for use later. Accumulators provide this memory.

The idea of accumulators is to define some processing that is to take place while a document is being sequentially processed: for example, a total that is to be accumulated. The semantics are defined in terms of a traversal of the tree representing the document, with each node being visited once before traversing its subtree, and again after processing the subtree. On each visit (both the pre-descent visit and the post-descent visit) it is possible to define rules that are activated, whose effect is to compute a new value for the accumulator based on the previous value and the data visible at the node being processed. The expression used to compute the new value must be "motionless", that is, it must be computable without changing the current position of the streamed input.

Accumulators have a procedural feel to them, because the state of the accumulator is modified as the document is processed. Nevertheless, they are functionally "clean": the value of an accumulator at a particular node is a pure function of the node. Functional programming enthusiasts will recognize accumulators as being very similar to the "fold" operation that is often used to apply an operation to every item in a sequence, in turn.

An accumulator defines the names of two functions: the pre-descent function and the post-descent function. These functions can be called supplying a node as argument, to give the value of the accumulator for that node before processing its children, and after processing its children. Sometimes the only value of interest is the post-descent value for the document node, that is, the value of the accumulator after the entire document has been processed.

Accumulators (like keys) are not associated with a specific source document; in principle they apply to every source document, and it's up to the implementation to find optimizations that avoid the cost of evaluating them for a document where they aren't needed. Saxon has two completely separate implementations, one for streamed documents and one for unstreamed documents. Accumulators are actively evaluated for every streamed document, but for non-streamed documents, they are evaluated only on request (the first call of an accumulator function for a particular document triggers evaluation of the accumulator up to the node in question. The value of the accumulator for each non-streamed node is stored along with the node, using a data structure that allows for the fact that the accumulator value will often change only occasionally as you proceed through the document.

The XSLT 3.0 specification contains a number of examples illustrating the use of accumulators, and studying these examples is probably the best way to gain an understanding of the feature.