Clojure + Music Notation

20 views
Skip to first unread message

PM

unread,
Dec 21, 2009, 4:09:03 PM12/21/09
to Clojure
I'm a new Clojure user. I've been working my way through SICP and the
videos that accompany it, and I've also read the Clojure book. I
already do a lot of work with a music notation library (JMSL) in Java,
and I'd like to see if I could do some tasks more simply in Clojure.

One of the issues I run into is how to represent a musical score using
immutable objects. A naïve representation of a score might look
something like:

- Score contains measures
- Measures contain staves
- Staves contain tracks
- Tracks contain notes
- Notes contain articulations, dynamics, and other extras.

I've mocked it up before using structmaps, but I get stuck when it
comes to doing all this with immutable structures. Immutability is no
problem for a system like Lilypond where the score data is fixed when
the program runs, but in a system where some of these items may be
changing as the score is edited, how do you handle that? Returning a
new note via assoc implies that a track's contents would also change,
making it a new track, and so on up the chain. (?)

I have to be missing something, and probably something glaring. I
understand that I could easily do this with mutable code, but if
anyone could provide some insight into this, it would be greatly
appreciated.

James Sofra

unread,
Dec 21, 2009, 10:36:01 PM12/21/09
to Clojure
HI,

You could look at the Zippers library http://clojure.org/other_libraries
which is useful for editing nested structures in a functional way...
or perhaps you might use one of the Clojure reference types to hold
the notes?

Cheers,
James

Miron Brezuleanu

unread,
Dec 22, 2009, 7:24:25 AM12/22/09
to clo...@googlegroups.com
Hello,

On Mon, Dec 21, 2009 at 11:09 PM, PM <peter.m...@gmail.com> wrote:
> I'm a new Clojure user.  I've been working my way through SICP and the
> videos that accompany it, and I've also read the Clojure book.  I
> already do a lot of work with a music notation library (JMSL) in Java,
> and I'd like to see if I could do some tasks more simply in Clojure.
>
> One of the issues I run into is how to represent a musical score using
> immutable objects.  A naïve representation of a score might look
> something like:
>
> - Score contains measures
> - Measures contain staves
> - Staves contain tracks
> - Tracks contain notes
> - Notes contain articulations, dynamics, and other extras.
>
> I've mocked it up before using structmaps, but I get stuck when it
> comes to doing all this with immutable structures.  Immutability is no
> problem for a system like Lilypond where the score data is fixed when
> the program runs, but in a system where some of these items may be
> changing as the score is edited, how do you handle that?  Returning a
> new note via assoc implies that a track's contents would also change,
> making it a new track, and so on up the chain. (?)

Are you worried about data structure copying? Because I think you
shouldn't be. Clojure's persistent data structure minimize copying,
and besides, even in older languages this kind of update has a
'functional flavor'.

For instance, in K&R's "The C Programming Language", 1st edition,
there is an example about counting words which uses a binary tree to
ensure quick access to a word. So this is C, imperative, with manual
memory management. But what they do is reuse subtrees a lot (If one
has to add a word to a tree, it's either added to the left or the
right subtree, and the unmodified subtree is just reused - this means
that, barring unbalancing of the tree, when modifying a tree with N
elements, they actually only change/rebuild Log2(N) nodes, and the
rest is reused).

The same happens a lot with data structures in functional languages -
the simplest example being consing, which creates a new list from an
old list, but it's all a O(1) operation because the old list is just
reused/linked to.

With nested maps in Clojure it's the same. If you use update-in or
assoc-in to update a note, the score is mostly the same, because most
of the old score is reused (even though it's a 'new one' - which makes
sense). <=== so try using update-in, assoc-in and get-in and maybe
your data structure will be easier to manipulate. Zippers are great
too, as James Sofra suggests.

I hope I managed to describe what you were missing, I probably used
too many words in the attempt :-)

--
Miron Brezuleanu

Meikel Brandmeyer

unread,
Dec 22, 2009, 7:39:32 AM12/22/09
to clo...@googlegroups.com
Hi,

Am 21.12.2009 um 22:09 schrieb PM:

> I've mocked it up before using structmaps, but I get stuck when it
> comes to doing all this with immutable structures. Immutability is no
> problem for a system like Lilypond where the score data is fixed when
> the program runs, but in a system where some of these items may be
> changing as the score is edited, how do you handle that? Returning a
> new note via assoc implies that a track's contents would also change,
> making it a new track, and so on up the chain. (?)

Besides zippers you might also want to look update-in and assoc-in. They work both with maps and vectors.

(update-in score [:this-measure :that-stave :the-other-track] modify-track with additional args)
(assoc-in score [:this-measure :that-stave :a-new-track] some-new-track)

Sincerely
Meikel

Mark Engelberg

unread,
Dec 22, 2009, 11:33:59 PM12/22/09
to clo...@googlegroups.com
Despite what others have said, I'm going to chime in and say that I
think an immutable hierarchy is going to be awkward for the use case
you describe.

In your example, notes are buried fairly deep in a hierarchy. Now if
you always tend to make manipulations by starting from the root score
node and working your way down the hierarchy, immutability works just
fine. But if you want to modify a note that somehow you've stored in
a separate list of notes, or the user has clicked on the note, or some
other kind of interaction where the note is being considered
separately from the rest of the hierarchy, you've got a problem.
update-in and assoc-in are only useful if you know the route from the
root score node to the note. And that information might not be
readily available. Similarly, the zipper library is only useful if
you've arrived at a given note through a series of zipper-style
navigations down the hierarchy. So I really doubt that either of
these proposed solutions will do you much good if you need to
frequently manipulate notes without drilling down to get to them.

I think you have two basic options:

1. Use refs. A score/measure/note/etc. are each refs containing the
appropriate struct. This is basically like using pointers in a
typical language, but you have the advantage of using transactions to
make a bunch of simultaneous updates where other threads will never
see the tree in an intermediate state.

2. Each struct for score/measure/note also contains a "name" field
which contains a unique name. Maintain a big map of all names to
structs. Scores do not directly link to measures, but instead contain
the *names* of the measures. To drill down from a score to the
measure, you must look up the measure by name in the big map. To
update a note, you just assoc the note name with the new note object
in the big map. This method is fully immutable. Updating a note
without knowing the full route through the hierarchy is easy because
the objects are not directly coupled. The big map of names to structs
is a persistent object that essentially can give you a snapshot
history of all changes that are made. The main price you pay is that
drilling down through the hierarchy becomes a bit slower because you
must always look up the names in the map.

Reply all
Reply to author
Forward
0 new messages