Representing and manipulating implicit state with XSLT
Added by Karim Ratib almost 2 years ago
I am faced with a recurrent issue in writing transformations for MusicXML, the W3C music interchange format.
MusicXML aims at representing music scores, both visually for human performers, as well as for machines that process the score further e.g. to transform them to MIDI.
For the sake of this discussion, the processing unit of a MusicXML score is the measure. The feature I am struggling with is the handling of measure attributes that are implicitly carried over to the next measure, without being explicitly re-declared at each subsequent measure. For example, the [key signature remains in effect unless it is explicitly changed.
I'm currently using accumulators to represent each piece of state that needs to be carried over, as per https://github.com/infojunkie/musicxml-mma/blob/0430265ff69e4b94f91d8bed46bddbbe04387f91/musicxml-unroll.xsl#L19-L36
The requirement it to only emit an attribute declaration when the corresponding attribute changes from its value at the previous measure. For this, I have to write a lot of repeated code:
- Each attribute has it own template parameter to remember the previous value https://github.com/infojunkie/musicxml-mma/blob/0430265ff69e4b94f91d8bed46bddbbe04387f91/musicxml-unroll.xsl#L70-L75
- Each attribute has its own accumulated state https://github.com/infojunkie/musicxml-mma/blob/0430265ff69e4b94f91d8bed46bddbbe04387f91/musicxml-unroll.xsl#L70-L75
- I need to compare the 2 values before emitting a declaration https://github.com/infojunkie/musicxml-mma/blob/0430265ff69e4b94f91d8bed46bddbbe04387f91/musicxml-unroll.xsl#L116-L134
- The measure template needs to be applied recursively, not linearly, because of the logic of jumping and looping. I then need to send each piece of state separately: https://github.com/infojunkie/musicxml-mma/blob/0430265ff69e4b94f91d8bed46bddbbe04387f91/musicxml-unroll.xsl#L116-L134
My question is: is there a way to reduce the amount of code needed? Especially as I add new pieces of state that need to be handled similarly.
Replies (4)
Please register to reply
RE: Representing and manipulating implicit state with XSLT - Added by Karim Ratib almost 2 years ago
Here's the key attribute link that I missed in the above.
RE: Representing and manipulating implicit state with XSLT - Added by Karim Ratib almost 2 years ago
An important distinction here, as I re-read my post: by "attribute" I mean a characteristic of the musical measure, as opposed to the technical term denoting a property attached to an XML element. Apologies for the confusion this may cause.
RE: Representing and manipulating implicit state with XSLT - Added by Michael Kay almost 2 years ago
A fasinating problem.
It's not immediately clear to me why you are carrying the state forward both using template parameters and using accumulators. I would have thought they were alternatives.
Rather than using sibling recursion over the measures as you are doing, I typically tend to use xsl.iterate
. One of the differences is that on each iteration you only need to set the parameters that have changed since last time. With sibling recursion you can achieve the same thing using tunnel parameters. But perhaps that's not such a big deal.
Another thing that might simplify the code is to bundle all the "attributes" into a map, passed as a single parameter (or accumulator value) rather than maintaining a separate parameter for each "attribute".
Another way to maintain state that is often overlooked is to use memo functions. You could implement a recursive function to compute the effective key signature for a measure as the explicit key signature for that measure if present, or the effective key signature of the previous measure otherwise; and if this is implemented as a memo function, it will be effectively stored as implicit state associated with each measure, and will compute in constant time. Not actually very different from accumulators, but a bit more flexible. (However, I need to check the implementation status of memo functions in SaxonJS).
And there's always the old-fashioned way of doing it: run a preprocessing pass over the data that stores the implicit properties of each measure redundantly as explicit properties. and then in a second pass all the properties are now explicit.
RE: Representing and manipulating implicit state with XSLT - Added by Karim Ratib almost 2 years ago
Thank you for your reply. I was indeed hoping to pique the interest of the community with what I think is a common issue.
It's not immediately clear to me why you are carrying the state forward both using template parameters and using accumulators. I would have thought they were alternatives.
This may be due to my lack of understanding of the processing model of accumulators. My understanding is that an accumulator rule will be invoked for each matching node, exactly once for each.
In the example of a musical key signature, I use the accumulator to store the key signature for each visited measure - the value will either be a copy of the previous key, or the new key if one is declared in the current measure.
But I don't want to emit a key signature at each measure during the transformation. I just want to emit a key if it has changed from last measure. This is where it gets tricky, as per the rules of music theory. There are actually two cases where the key will change:
- If a new key is explicitly declared as per the accumulator description above
- If a musical loop jumps back to a measure that occurs within a different key signature than before the looping occured
It is to detect the key change that I am carrying around a template parameter. The accumulator gives me the key signature for the current measure, but I could find no mechanism in the accumulator alone that would remember the previously emitted key signature (remembering that it's not necessarily the previous measure in document order).
Thanks for the various suggestions, I will dig into them.
Please register to reply