Although it was a little quiet in this blog during the last weeks, this doesn’t mean that we aren’t working hard on the next iteration. Haydn (Iteration 50) is really a hard nut to crack! Currently we are completely replacing the core of Zong! which allows to store and manipulate musical data. While this core part was heavily relying on side effects and mutable data, we are now moving to a core that contains only immutable, persisent data structures and embraces a much more functional style.
The idea to do that arised when I was learning Clojure. Although we do not use Clojure (yet), since it is still too complicated to call from the Java side, it is always a good idea to learn new languages and to gain insight into different programming concepts. This makes you even a better programmer in the language you are coming from! But now, what are the advantages of a functional core?
One good reason is the testability. Since there are no side effects, it is much easier to understand and debug the code. The same input always results in the same output, without influence of outer effects. Another reason is concurrency: Persistent data structures are thread safe by design, and it is never necessary to create defensive copies of your objects, because it is guaranteed that they can never be changed. But the biggest advantage from a user’s point of view is undo/redo functionality. While we wracked our brains how to undo side-effect based actions up to now, it is now trivial to undo each action: Just remember the root object before performing the action. This is brain-dead easy!
Unfortunately, Java was not designed for using immutable objects, so the code for implementing these classes is not as nice as it could be like when using Clojure for example. Neverless it is possible and often results in much nicer code when using these classes. We decided to use the pcollections library and it does a great job so far! Although it is still quite unknown, I am sure that it will get more attention soon, because the advantages of persisent data structures are really groundbreaking in my eyes.
How does the persistent core works? Basically, we have a tree of musical classes with the
Score class as its root. It is immutable (nothing can be changed), so no backward links are possible. This is kind of a problem, since for example a chord belongs to a slur and a slur contains chords. This can not be directly modelled with immutable data structures. Therefor, we have a big hashtable at the root which save all
Globals (beams, slurs, ties, attached elements like dynamics, musical positions of each element for very fast lookup, and so on). The staves, measures, voices and its element are represented in a tree structure like one would probably expect it.
Let’s say we want to add a note to an existing chord, which is beamed. Therefor, we create a new score, based on the old one (and of course sharing as much data as possible, which is never a problem since everything is immutable :)). When “changing” a chord, we also have to “change” its voice, then the measure the voice belongs to, then the staff the measure belongs to, and so on, up to the root. After that we have to update the
Globals since our chord now has a new reference of course. This sounds more complicated at first, but the hierarchy of the elements in the tree is quite flat, and we are not using (much) more memory than we would need for the side-effect
based solution. Remember: While it is more efficient to work with side effects when performing some action, it may be a nightmare to undo it again! This is absolutely trivial using persistent data structures: Just remember the
Score instance, perform your changes, and if you want to undo them again, just take the old
Score instance. Cool, isn’t it? 8)
The changes are so radical, that I’m already working for more than 50 hours to integrate them into the other Zong! projects. There are still 1229 compile errors to solve… But I’m sure that the new core will bring Zong! a very big step forward – also because we solved the tremendous undo/redo problem by the way.