OOLOI.ORG
Menu

OOLOI

An Organism Evolved.

OVERVIEW

DOCUMENTATION

NEWSLETTER

Music for Computer Scientists

10/1/2026

0 Comments

 
Picture
​A few weeks ago I wrote a post explaining computer science terminology to musicians. This is the inverse: an explanation of music notation for programmers who assume the domain is simpler than it is.

That assumption is common. It's also expensive. Most notation software has been written by very competent engineers who underestimated the problem. The result is systems that work impressively until they don't – at which point they fail in ways that seem arbitrary to users but are, in fact, architectural.

This post explains why.

Notation Is Not MIDI with Graphics

Picture
The most damaging assumption programmers bring to this domain is that music notation is a visual layer over time-stamped events. Work with audio software, game engines, or digital music for long enough, and this seems obvious: notes have start times, durations, and pitches. Notation just draws them nicely.

This is wrong in a way that corrupts everything downstream.

Consider two representations of identical duration:

  • (a) quarter note
  • (b) dotted eighth tied to sixteenth

These produce the same acoustic event. They do not mean the same thing. The first is a single rhythmic impulse sustained. The second implies a specific re-articulation point encoded in the notation itself. Performers read these differently. Conductors might even beat them differently. The semantic distinction is not ornamental.

MIDI cannot represent this distinction. It knows only NOTE_ON, NOTE_OFF, and duration. Notation systems built on MIDI-like assumptions inherit this blindness and spend their entire existence fighting it.

Music notation encodes musical meaning, not acoustic events. The implications of this single fact occupy the rest of this post.

Time Is Not Integers

Picture
Programmers reach instinctively for integer representation. MIDI uses ticks – typically 480 or 960 per quarter note. DAWs use samples. The assumption is that any temporal precision can be achieved by choosing a sufficiently small quantum.

This is false for music notation.

Consider a simple case: a quarter-note triplet against regular eighth notes. The triplet divides the beat into thirds; the eighths divide it into halves. To represent both exactly in a tick-based system, you need a tick count divisible by both 2 and 3. Fine: use 6 subdivisions.

Now add a quintuplet in another voice. You need divisibility by 2, 3, and 5: LCM = 30.

Now consider what real music does. Chopin writes nested tuplets. Ferneyhough writes 7:6 against 5:4 against 3:2. Elliott Carter writes simultaneous streams at different tempi. The required tick resolution grows combinatorially:

Picture
where di are tuplet denominators and k is your base resolution. For complex contemporary music, this can exceed 10^6 ticks per beat, at which point integer overflow becomes a real concern and accumulated floating-point error in tempo calculations produces audible drift.

The solution is obvious once stated: rational arithmetic. A quarter-note triplet is exactly 1/3 of a half note. No approximation. No accumulated error. The arithmetic is exact because the representation matches the domain.
​
  • triplet quarter = 1/3 × 1/2 = 1/6 of whole note
  • regular eighth  = 1/8 of whole note

These don't share a finite decimal expansion. They don't need to. Rationals are closed under the operations music requires.

Clojure provides this natively. Most notation software doesn't use it. The consequences appear everywhere timing matters – which in notation is everywhere

The Structure Is Not a Tree

Picture
Programmers expect hierarchical data. XML, JSON, ASTs, DOM trees – the mental model is that complex structures decompose into nested containers. Parent nodes own child nodes. Traversal is well-defined.

Music notation has multiple simultaneous hierarchies that do not nest cleanly.

A single note participates in:
  • Metric structure: beat → measure → section
  • Rhythmic grouping: tuplet → beam group → phrase
  • Pitch organisation: voice → chord → harmonic progression
  • Measure layout: measure → staff → instrument → system → page
  • Notational conventions: slur, dynamic hairpin, articulation group

These hierarchies intersect. They do not nest. Consider cross-staff notation: a chord where some notes appear on the treble staff and others on the bass. Which staff 'owns' the chord? Neither. Both. The question assumes a tree structure that doesn't exist.

Or consider voice exchange: soprano and alto swap pitches mid-phrase. The melodic line (a horizontal relationship) contradicts the voice assignment (a vertical relationship). Both are musically meaningful. Neither subsumes the other.

Grace notes exist precisely at the intersection of conflicting hierarchies. They have pitch (placing them in a voice) but steal time from adjacent notes (making their metric position contextual). They may or may not take accidentals from the main-note context. The rules vary by historical period.

Systems that force music into a single hierarchy spend enormous effort on edge cases that are only 'edge' cases because the data model is wrong.

Context Is Non-Local: The Accidental Problem

Picture
​Here is a concrete example of why music notation is harder than it looks.

Question: Does this F need a sharp sign?

Answer: It depends on:
  1. The key signature (established possibly pages earlier)
  2. What happened earlier in this measure in this voice
  3. What happened earlier in this measure in other voices on the same staff
  4. What happened earlier in this measure in other staves (in keyboard music, an F♯ in the left hand may affect an F in the right)
  5. Whether there's a tie from the previous measure
  6. Whether this is a grace note (different rules apply)
  7. Publisher conventions (some always mark cautionary accidentals, some don't)
  8. Historical period (18th-century conventions differ from 20th-century)

A naive algorithm checks each note against previous notes in the measure. This is O(n) per note, O(n^2) per measure. Fine for simple music.

Now add multiple voices. The F in the soprano might be affected by an F♯ in the alto, depending on conventions. You need to track alterations across voices. If voices cross staves (as in keyboard music), the interactions multiply.

Now add simultaneity. What happens when two voices sound the same pitch at the same instant but with different accidentals? Yet another edge case difficult to catch when imprisoned in the Jail of Recursive Descent.

Now add grace notes. A grace note at the beginning of a measure might be notated at the barline but sound at the end of the previous measure. Does its accidental affect the next measure? Does the next measure's key signature affect it?

The dependency graph for accidentals is not a simple sequence. It's a directed acyclic graph with edges determined by temporal position, voice membership, staff assignment, and notational category. Correct handling requires topological sorting with domain-specific edge semantics.

Most notation software handles this with heuristics: rules-of-thumb that cover common cases and fail on uncommon ones. The failures aren't bugs in the usual sense. They're consequences of architectural decisions made decades ago when the problem was less well understood.

Ooloi handles this through what I call the 'timewalker' – a transducer that processes musical events in semantic order (not storage order) allowing full temporal context accumulation. The accidental algorithm becomes stateless over its input stream: given the same sequence of musical facts, it produces the same decisions. Deterministically. Every time.

The complexity is still O(n · v) for n events and v voices, but the constant factor drops dramatically because traversal and processing are separated. The transducer handles navigation; the algorithm handles meaning.

Concurrency Is Not Optional

Picture
​Here is where most notation software fails silently.

A modern computer has multiple cores. Users expect responsiveness: type a note, see it appear immediately. But notation isn't like a word processor where inserting a character affects only nearby layout. Changing one note can alter accidentals throughout a measure, reflow an entire system, or invalidate cached engraving calculations across pages.

The traditional approach is to serialise everything. One thread handles all mutations. The UI blocks or queues. This is why notation software freezes when you paste a large passage, or why collaborative editing remains essentially unsolved in this domain.

The problem deepens when you consider what 'correct' means during an edit. If a user changes an F♮ to an F♯ in measure 4, the accidental state of measure 5 depends on this change. But if the rendering thread is currently drawing measure 5, what does it see? The old state? The new state? Some inconsistent mixture?

Mutable state with locks doesn't solve this; it creates a new category of bugs. Deadlocks. Race conditions. Heisenbugs that appear only under specific timing. A very difficult work environment indeed, and one where diminishing returns becomes a real impeding factor.

The functional approach is different. If musical data is immutable, the question dissolves. The rendering thread has a consistent snapshot – always. The editing thread produces a new snapshot. There's no moment of inconsistency because there's no mutation.

This is what Software Transactional Memory provides in Clojure. Transactions compose. Reads are always consistent. Writes are atomic. The system guarantees that no thread ever sees a half-updated score.

The performance implications are substantial. Ooloi's STM handles heavy contention without the coordination overhead that serialised systems pay constantly. The bottleneck shifts from synchronisation to actual work.

Real-time collaboration becomes architecturally possible. Two users editing the same passage don't corrupt each other's state; they produce alternative futures that the system can merge or present as conflicts. This isn't a feature bolted on afterwards. It's a consequence of representing music correctly from the start.

Engraving Is Constraint Satisfaction

Picture
Ravel being difficult
​Programmers sometimes assume that once you have the musical data, displaying it is straightforward. Calculate positions, draw glyphs, done.

This underestimates the problem by roughly an order of magnitude.

Consider accidental placement in a dense chord. Multiple accidentals must avoid colliding with each other, with noteheads, with stems, with ledger lines, and with accidentals in adjacent chords. The placement rules aren't arbitrary – they encode centuries of engraving practice optimised for readability.

This is a constraint satisfaction problem. In the general case, it's NP-hard. Real systems use heuristics, but the heuristics are subtle. Gould's Behind Bars – the standard reference on notation practice – devotes many pages to accidental stacking alone.

Now multiply by every other engraving decision: beam angles, stem lengths, tie curves, slur routing, dynamic placement, tuplet brackets, ottava lines, system breaks, page breaks. Each interacts with the others. Change a stem length and the beam angle needs adjustment; beam ends need to follow sit-straddle-hang rules and might both move. Change a beam angle and adjacent voices may collide.

LilyPond's engraving quality comes precisely from treating this as a serious optimisation problem. Its page-breaking algorithm runs a variant of the Knuth-Plass line-breaking algorithm (developed for TeX). Its spacing uses spring-based constraint systems. The 'simple' rendering layer contains some of the most sophisticated code in the system.

Interactive notation software faces an additional challenge: these calculations must be fast enough for real-time editing. You can't spend ten seconds optimising layout every time the user adds a note. The tension between quality and responsiveness has driven much of the architectural evolution in this field.

Historical Conventions Are Not Bugs

Picture
​A programmer encountering music notation for the first time will find countless apparent inconsistencies. Surely these could be rationalised?

No. Or rather: not without destroying information.

A fermata on a note means 'sustain this note'. A fermata on a barline means 'pause here'. A fermata on a rest means something slightly different again. These distinctions emerged from practice. Composers use them. Performers understand them. A system that normalises them into a single 'fermata event' loses musical meaning.

Grace notes in Bach are performed differently than grace notes in Chopin. The notation looks similar. The performance practice differs. A system that treats all grace notes identically will produce correct output that performers find misleading.

Clefs change meaning by context. A C-clef on the third line is an alto clef; on the fourth line, a tenor clef. Same symbol, different staff position, different meaning. This isn't legacy cruft – orchestral scores routinely use multiple C-clef positions because different instruments read different clefs.

Mensural notation (pre-1600) has entirely different rules. The same notehead shapes mean different things. Accidentals follow different conventions. Barlines serve different purposes. A truly comprehensive notation system must handle this or explicitly disclaim it.

The temptation to clean this up, to impose regularity, to treat historical conventions as technical debt – this temptation must be resisted. These conventions encode musical knowledge. Destroying them in the name of consistency destroys the knowledge.

The Performer Is the Point

Picture
​All of this serves a purpose that has nothing to do with computers.

Notation exists to communicate with humans. Specifically: performers who bring interpretation, physical constraints, and centuries of inherited practice to their reading.

This isn't sentimental. It has engineering consequences.

A page turn in the wrong place forces a pianist to stop playing or memorise a passage. This is a functional failure, not an aesthetic preference. The page-breaking algorithm must allow this to be modelled.

Awkward beam groupings obscure metric structure. The performer reads beats through beam patterns. Wrong beaming means wrong emphasis. The beaming algorithm must model this based on the currnent time signature.

Unclear voice leading forces performers to decode the texture before they can interpret it. The system must make voices visually distinct in ways that match musical distinctness.

Engraving choices that are mathematically equivalent may not be performatively equivalent. Two layouts might use the same space and avoid all collisions, but one reads naturally and the other requires active deciphering. The difference matters.

This is why notation is not a solved problem. The specification is 'communicate musical meaning to human performers'. That's not a format spec. It's a design constraint that touches every decision in the system.

Conclusion

Picture
Music notation looks like a data format problem. It is actually a meaning representation problem with a millennium of accumulated context.

The systems that fail – and most fail in some way – do so because they treat notation as something to be processed rather than something to be understood. They model the symbols without modelling what the symbols mean.

The systems that succeed take the domain seriously. They represent time exactly. They handle multiple hierarchies without forcing false containment. They process context correctly even when context is non-local. They treat engraving as a real problem, not a rendering detail. They preserve historical conventions because those conventions carry meaning.

This is why Ooloi's architecture looks the way it does. Rational arithmetic isn't ideology; it's the only way to represent tuplets exactly. Immutability isn't fashion; it's the only way to guarantee that accidental decisions are deterministic. Transducers aren't showing off; they're the cleanest and most efficient way to process a semantic stream with accumulated context. STM isn't over-engineering; it's the only way to achieve both correctness and responsiveness without sacrificing either.

The domain dictates the tools. Not the reverse.

0 Comments

Twice

31/12/2025

2 Comments

 
Picture
Two weeks ago, the remembered alterations algorithm proved that accidental rendering could be solved deterministically. I wrote about needing to breathe, to get my head around it.

Today, I formally specified measure distribution as ADR-0037. The Knuth-Plass algorithm – the same dynamic programming approach TeX uses for paragraph breaking – applies directly to distributing measures across systems. Not through cleverness, but because Ooloi's five-stage rendering pipeline decouples concerns that traditional architectures leave coupled. By the time the distribution algorithm runs, collision detection and vertical coordination are already resolved. What remains is a one-dimensional sequence with scalar preferences. Textbook problem. Textbook solution.

The algorithm itself is not news. Knuth-Plass has existed since 1981.

The news is that this is the second time.

Two problems the industry treats as inherently heuristic – requiring manual correction, special cases, user-facing options to tune approximations – collapsed into straightforward algorithms. Same architectural properties both times: immutable data, rational arithmetic, explicit stage boundaries, semantic determinism before layout.

Once might be coincidence. Twice suggests the architecture itself is doing something.

For engravers, if this holds: no accidental weirdness. No measure distribution cleanup. No jitter when editing – changing bar 47 won't mysteriously reflow bar 12. Layouts that are globally optimal, not locally adjusted. Proportional spacing preserved by construction. Adjustments for taste, not necessity.

I keep writing 'if this holds' because the proof is mathematical, not yet empirical. The algorithm is sound; the question is whether real scores – Gesualdo madrigals through Berg's Orchesterstücke or Ligeti's Requiem – expose edge cases the staged pipeline doesn't fully decouple. The remembered alterations work is tested. Measure distribution needs equivalent validation.

But the pattern emerging is harder to dismiss than any single result. If two 'impossible' problems turn out to be straightforwardly solvable, how many others?

I don't know yet. That's the honest answer.

Roland Gurt's comment last week – about spacing instability in the programs he uses, where editing one element mysteriously reflows unrelated staves on following pages – started me thinking seriously about this problem. Thanks, Roland.

Happy New Year, everyone.
2 Comments

Computer Science for Musicians

21/12/2025

8 Comments

 
Picture
Roland Gurt, a musician and regular reader here, recently asked whether I could explain some of the computer science terminology that keeps appearing in these posts – terms like functional programming, immutability, and transducers – in layman's terms, and preferably from a musical point of view.

I was genuinely glad to be asked.

At this stage of Ooloi, nothing has yet been drawn on the screen. What has been happening is work on problems that sit underneath notation, problems that have been considered 'mostly solvable' at best since the 1980s. The deterministic accidental system I recently wrote about is one such problem. It looks abstract because it has to be. It deals with musical meaning before anything becomes visible.

This post is an attempt to explain why these terms keep appearing, and what they actually refer to, without turning this into a programming tutorial. It's written for musicians who are curious, not for programmers who already know the vocabulary.

Starting Without Baggage

​One reason Ooloi can attempt this at all is that it's a new system.

Most notation programs in use today have roots in techniques from the 1980s and 1990s. Internally, they tend to look surprisingly similar. They represent music in roughly the same way and rely on the same architectural assumptions: object-oriented design, mutable state, and large codebases written in C or C++.

Once a system has grown for decades on top of those assumptions, it becomes extraordinarily difficult to rethink its foundations without breaking everything at once. We saw Finale collapse in 2024 for precisely this reason.

There have been only two real departures from that lineage. One was Igor Engraver in the 1990s. The other was Dorico from 2016. Dorico has much in common with Igor Engraver in terms of musical data representation, and it made important advances. But even Dorico still rests on the technological assumptions of that era: object-oriented design, mutable state, and C++ at its core.

Ooloi puts all its money on a different assumption, namely that those older techniques fundamentally are architecturally inadequate for the problem of music notation as we now understand it. That inadequacy has consequences for users: accidentals behaving oddly, freezes and delays, limited scalability, no real collaboration, and poor use of modern multi-core machines.

​To address these problems, Ooloi uses an entirely different set of techniques.

A Genuinely Hard Problem Domain

Picture
​It also needs to be said very clearly that music notation, from a computer science perspective, is an extremely hard problem.

It's not 'a word processor for music'. That analogy has done lasting harm.

Music notation sits in the same general complexity class as compiler construction, symbolic mathematics systems, or advanced typesetting engines. It combines several difficult aspects at once: hierarchical structure, temporal meaning, long-range context, simultaneous layers, and a visual representation that's not a straightforward reflection of the underlying data.

A compiler reads a stream of symbols, builds meaning from context, enforces consistency rules, and produces deterministic output. Music notation does all of that, and then adds engraving conventions, historical practice, performer expectations, and visual clarity on top.

If you approach this domain with tools designed for relatively static documents or interactive graphics, you end up fighting the problem instead of modelling it. The result is systems that work impressively most of the time, but fail in edge cases, which in music aren't rare.

Determinism as the Musical North Star

Picture
​Before any technical terminology enters the picture, there's a simple musical requirement.

If you engrave the same piece twice, you should get the same result twice.

Not roughly the same. Not 'close enough'. The same.

Most notation software quietly gives up on this requirement. Instead, they rely on rules of thumb: 'this usually works', 'this is how engravers tend to want it', 'clean this up manually if it looks odd'. Musicians have learned to live with that because, for a long time, there was no alternative.

From a musical point of view, this is deeply unsatisfactory. It means the system doesn't really know what it's doing. When things get complicated, it starts guessing. And once guessing enters the picture, everything downstream becomes fragile.

Determinism simply means that given the same musical input, the system makes the same musical decisions every time.

Immutability: Trusting what has Already Happened

Picture
​Imagine packing a suitcase before a trip. You put clothes, toothpaste, shoes inside. Once the suitcase is closed and checked in, you make decisions based on what you packed. You don't buy extra clothes at the airport because you trust they're already there.

Now imagine that, while the suitcase is in transit, someone silently changes its contents. In a trivial case this is merely annoying. In a serious system – air-traffic control, medical records, nuclear command – it's catastrophic: people might die.

To reason about anything, you must be able to trust the past.

In computer science, immutability means this: once a thing has been created, it never changes. If you need a slightly different version, you don't alter the original. You create a new one. The old one remains exactly as it was.

This sounds wasteful until you realise that modern systems can do this efficiently by sharing structure internally. This is where a bit of Harry Potter magic comes in. If someone changes one shirt, they get a new suitcase with just that one shirt changed, while you still have your original suitcase, untouched and with the original shirt still inside.

In music notation this matters because the domain is full of remembered context. Accidentals depend on what happened earlier. Key signatures establish expectations. Voices interact. If earlier musical facts can silently change while later decisions depend on them, determinism collapses.

Functional Programming: same Input, same Result

​Most people already understand from school what a function is.
Picture
If x is 100, the result is always 101. Not sometimes. Always.

That property is determinism.

Now imagine that x is not a number but a suitcase. If the function's result depends on what is inside the suitcase, you must be absolutely certain that the contents haven't changed since last time. And for this, the suitcase needs to be one of our magical suitcases. Otherwise, calling the same function twice with what looks like 'the same input' might silently produce different results. The consequences range from a missing accidental to obliterating Tokyo.

Functional programming is simply the discipline of insisting on this property at scale. Functions don't reach out and change things behind your back. They don't depend on hidden mutable state. They take inputs and return results without any such surprises.

In Ooloi this matters because musical decisions must be reproducible. If accidental decisions or tie behaviour depend on invisible state changes, the system cannot be fully trusted, even if it usually looks right.

The Score as a Semantic Stream

Picture
A central idea in Ooloi is to treat the score as something that flows.

Music is read from left to right. Context accumulates. Decisions depend on what has already passed. Accidentals are remembered and later forgotten. Grace notes steal time from what they follow. Tuplets locally distort time.

This is not a static tree that you occasionally inspect. It's musical meaning - semantics - unfolding over time.

Once you accept that, you need a way to traverse this stream efficiently without constantly rebuilding lists or retracing paths.

​And this brings us to transducers.

Transducers: Separating the Route from the Work (in the Jail of Recursive Descent)

Picture
​Imagine a high-security prison complex with multiple buildings, floors, and endless cells. Every day, a warden must traverse the entire structure to take stock of what's inside. And he must do it in the same order each day, just as the notes in a piece must be played in the same order each time.

Most of the warden's effort goes into navigation: remembering routes, tracking where he's been, writing lists just to keep his place. He does this over and over again, every day.

A transducer is like giving the warden Ariadne's thread. But again, there's magic involved: the thread weaves from cell to cell of its own accord. The route through the maze is fixed and reliable. Navigation stops being the problem. 

Better still, the warden doesn't even have to walk, thanks to more magic: the thread acts like a firehose and sends back all the information to the origin. The warden can sit in his office while the traversal happens and receive a continuous stream of information about what's found along the way. He can devote his entire attention to the meaning of the data, not how to walk the prison complex.

The crucial point is that traversal and processing are separated. The route is handled once. The real work happens as data flows past, without intermediate lists or repeated navigation.

In Ooloi, transducers allow musical meaning to be processed in exactly this way. The system doesn't build a large structure and then analyse it afterwards. It reacts to musical facts as they appear, deterministically and efficiently.

This is what a transducer is. Then there's even more wizardry involved in that several transducers can be stacked on each other to perform even more abstract operations. The path has been abstracted away, the distractions are gone, and the flow of  meaningful musical data is all that matters.

Vocabulary

​These terms aren't ideology, and they're not badges of virtue. They're simply the most precise language I've found for describing how musical meaning behaves when you try to handle it rigorously.

The music comes first. The vocabulary follows.
8 Comments

Breather

17/12/2025

5 Comments

 
Picture
If anyone wonders what the above is, it's the deterministic solution to a hairy engraving computer science problem nobody thought was solvable. It's been like that since the 80s. Everybody has just kind of capitulated and lived with a 'it's 95% okay, the rest is impossible' type of mindset and then, as one must in that situation, used rules-of-thumb to reach that 95%. Which is a lot, and a very respectable achievement.

However, it turned out that Functional Programming and immutability - and non-consing push transducers - could solve the problem deterministically. For the first time ever.

​I think it holds. We shall see.

What it would mean for notation? No accidentals weirdness. No cleanup. Consistency. Adjustments for taste, not necessity.

The power of Clojure, of treating the piece as a transforming semantic stream, and of not working with OOP and mutable state.

Now I need to sit down and breathe for a while. Time to go into Hammock Mode. I need to get my head around this.
5 Comments

Nun denn, allein!

16/10/2025

0 Comments

 
Picture
'Well then, alone!' – Elektra's cry when Chrysothemis refuses to help her. Not triumphant independence, but desperate necessity fused with unwavering resolve. The isolation isn't chosen; it's forced by the impossibility of finding anyone who shares her singular purpose. Orestes isn't likely to materialise.

There was a functional programming conference in Stockholm recently. I'm sure it was excellent. I didn't attend. I should probably have been there – Ooloi is built in Clojure, after all, and finding collaborators would be useful – but I felt conflicted, and that conflict revealed something I'd been avoiding: the FP community cannot help me, and I don't need it anyway.

​Sect Dynamics

​I'm disappointed with the functional programming community. I was expecting higher-level thinking – freer thinking, commensurate with the intellectual freedom Clojure itself offers – but the atmosphere proved to be a shallow puddle of sectarianism​. That probably has its reasons – being marginalised as a community is probably one of them – but the end result remains unchanged.

The patterns are unmistakable. Knowledge as gatekeeping: the endless monad tutorial phenomenon, where every advocate believes they can explain monads better than everyone else, typically through increasingly baroque metaphors involving burritos, space suits, or elephants. This isn't pedagogy; it's ritual initiation. The complexity serves a social function – maintaining boundaries between insiders and outsiders.

Purity as virtue signalling: debates about whether using `IO` makes you impure, whether exceptions violate functional principles, whether mutation in bounded contexts is acceptable. These discussions frame technical trade-offs as moral categories, as though architectural design were a moral discipline rather than an engineering one. The language reveals it – clean, pure, disciplined versus dirty, impure, undisciplined. This is religious vocabulary applied to software engineering.

Terminology as tribal marker: deliberate retention of academic terminology when simpler terms exist. Endofunctor, catamorphism, anamorphism when 'map over containers', 'fold', 'unfold' would suffice. The obscurity is the point – it establishes hierarchy and demonstrates membership.

The emphasis falls on mathematical elegance rather than problem-solving. The question isn't Does this help ship software but Is this theoretically sound. People who can recite monad laws but have never shipped a product receive more status than developers applying functional patterns to solve actual problems.

Then there's the missionary behaviour: the conviction that imperative programmers need conversion. The framework isn't Here's another useful tool but You're doing it wrong until you see the light. This creates antagonism rather than adoption.

Being marginalised as a community probably explains some of this – defensive posture manifesting as increased boundary enforcement, which creates insider/outsider distinctions, which enables status hierarchies based on doctrinal purity. But understanding the cause doesn't change the result, and it doesn't make the behaviour intellectually rigorous or practically useful.

The Clojure Exception

Clojure largely escaped this because Rich Hickey explicitly rejected purity culture. 'It's acceptable to use Java libraries'. 'Mutability in bounded contexts is fine'. 'Solve problems first'. The Clojure community focused on what you can build, not on arcane knowledge as status marker. This produced broader adoption without compromising functional principles.

This is why I chose Clojure for Ooloi in the first place. But even within that pragmatic oasis, the broader FP community dynamics leak through. The conference I didn't attend would have featured both kinds of people – those interested in building things and those interested in doctrinal purity – and I couldn't predict which would predominate.

The Intersection Problem

​Here's the substantive issue: finding Ooloi collaborators in FP communities is statistically improbable because very few people occupy my intersection point between various disciplines.

Music notation requires an understanding of compositional structure, engraving conventions, and how musicians actually work. Functional architecture requires a sophisticated understanding of immutability, higher-order functions, transducers, STM transactions, and compositional patterns. Backend infrastructure requires a willingness to work on unglamorous problems like endpoint resolution and temporal traversal rather than visible features, and in Ooloi's case, an understanding of server technology and secure cloud operations.

The population at that intersection is approximately one.

FP communities might yield people who appreciate my transducer implementations or STM transaction handling. But they won't understand why endpoint resolution for slurs matters, how temporal traversal serves musical structure, or what makes intelligent engraving different from geometric placement. The domain expertise is orthogonal to FP community concentration.

The inverse holds equally: musicians who understand notation deeply rarely have the architectural sophistication to work on Ooloi's core, and even fewer would find satisfaction in building infrastructure rather than using tools.

I've worked outside the FP community all my life. Functional programming is a tool, not a (monadic) religion. (And why are monadic and nomadic so similar?) Why join the community now, when the benefits are unclear and the costs palpable?

Consilium Latinum

​The technical response is what I call the Latin strategy: making Ooloi's core a stable foundation for a plugin ecosystem. Build the architectural core once in Clojure, then let developers in other JVM languages contribute via plugins without needing to understand the underlying functional implementation. I've written about this approach in Penitenziagite!, so I won't rehearse it here.

Elektra or Quixote?

Picture
The psychological question is whether this makes me Elektra or Don Quixote.

Elektra confronts a real murder, real injustice, a legitimate need for action that others refuse. The isolation comes from their cowardice or pragmatism, not from her misunderstanding of reality. The task is achievable and gets completed. The tragedy is the psychological cost, not the validity of the purpose.

Quixote confronts imaginary problems with obsolete ideals, mistaking windmills for giants. The isolation comes from a fundamental disconnect with reality. The task is impossible because it's based on delusion. The comedy (later tragedy) is that the quest itself is meaningless.

The distinction depends on whether the problem is real. Do musicians actually need what Ooloi provides? If existing notation software genuinely fails at problems Ooloi solves, then Elektra. If musicians are adequately served by current tools, if the architectural sophistication I'm building doesn't translate to problems they actually experience, then Quixote.

But there's a third option beyond tragic obsession and delusional quest. I'm building something architecturally excellent because I can, because it interests me, because functional approaches to musical structure are intellectually satisfying. The architecture might be elegant, but it's not worth psychological dissolution.

The Latin model suggests I've already chosen this third path. I'm building core infrastructure well, documenting it properly, then making it available via plugin architecture that assumes others might have different needs. That's craft separated from identity.

Not Dancing to Death

Elektra's tragedy is total consumption by purpose. She becomes nothing but the task, and when it completes, there's nothing left because she permitted no existence beyond vengeance. She dances herself to death.

I'm certainly not doing that. Ooloi is a project, not my entire existence. Sustainable completion means finishing the backend, documenting it clearly, releasing it, and then moving on. The work stands independently; I remain separate from it.

I'll finish Ooloi's core architecture working alone, not because I prefer isolation, but because collaboration at this intersection point is impractical. The resolve comes from accepting reality rather than pretending community exists where it doesn't.

The backend is complete. The transducer-based timewalker is fast, tight, and efficient. Endpoint resolution handles slurs and ties correctly. Nearly nineteen thousand tests pass. Vector Path Descriptors enable elegant client-server communication.

Then comes plugin architecture, and seeing whether anyone finds Ooloi useful. If they do, excellent. If they don't, I built something architecturally sound and learned what I needed to learn.

Either way, the work speaks for itself. And I continue existing beyond it.

Nun denn, allein!
Picture
0 Comments

Penitenziagite!

7/10/2025

0 Comments

 
Yes, another technical post. But this one explains why Ooloi doesn't demand you become a programmer to use it, or learn Latin to extend it for your specific needs. The architecture matters precisely because it removes barriers rather than creating them.
Picture
​In Umberto Eco's The Name of the Rose, a mad monk wanders through the monastery corridors shouting 'Penitenziagite!' – corrupted Latin mixed with vernacular, incomprehensible noise that might be prophecy or might be madness. Communication fails not from lack of content, but from linguistic confusion. The message is lost in translation.

Software architectures shout 'Penitenziagite!' constantly, and we've grown so accustomed to the noise that we mistake it for communication.

The Language Impedance Problem

​When you write a plugin for a system, you shouldn't need to learn the implementation language. That seems obvious, yet most software makes exactly that demand. Functional programming libraries leak monads into Java APIs. Object-oriented frameworks force functional concepts into awkward method chains. Every language boundary becomes a barrier, every abstraction a translation exercise.

The pattern is familiar:
  • Methods that expose internal implementation details externally
  • APIs that assume you think in the framework's paradigm
  • Documentation that requires understanding the core's language to extend the edges
  • Forced mental context-switching when crossing boundaries
  • 'Just learn our language' presented as the solution rather than the problem

This isn't malice. It's accidental linguistic imperialism – systems that never considered the difference between internal precision and external accessibility.

The Monastic Pattern

Medieval monasteries preserved knowledge in Latin – a dead language, deliberately removed from common speech, chosen for precision and permanence. Yet they didn't demand everyone learn Latin to benefit from monastery medicine or improved agriculture. The knowledge stayed pure in the centre; the benefits propagated outward in the vernacular.

Ooloi follows this pattern.

The core is written in Clojure. This isn't negotiable, because the hard problems in music notation software require immutability, structural sharing, and proper concurrency. Functional programming isn't a preference; it's the only approach that doesn't collapse under its own compromises.

But plugins can be written in Java, Kotlin, Scala, or any JVM language. Not as second-class extensions with limited capabilities, but as first-class citizens with full API access, equal performance, and no artificial limitations. The JVM interop means there's no penalty for crossing the boundary – your Java plugin operates with the same guarantees as code written in Clojure.

This arrangement has three parts:

I: The Scriptorium (Clojure core) – Where the hard problems are solved with uncompromising discipline. Immutable data structures provide structural sharing. Temporal coordination via the timewalker ensures correct musical semantics. STM enables proper concurrent editing. Zero-allocation hot paths ensure performance. This is where craft is mandatory, not aspirational.

II: The Library (canonical plugins in Clojure) – Reference implementations showing how the architecture should be used. Teaching by example, maintaining standards, preserving patterns for others to study.

III: The Gate (JVM plugin system) – The boundary that speaks idiomatically in every JVM language. Immutability guarantees propagate transparently. Plugin developers work naturally in their chosen language whilst benefiting from the rigorous core.

Why This Structure Works

​The core cannot compromise. If mutability seeps in, if temporal coordination is abandoned for convenience, the whole thesis fails. The hard problems must be solved correctly, once, in the protected centre.

But the perimeter cannot be closed. If only Clojure developers can extend Ooloi, adoption remains limited to those willing to learn functional programming. The architectural advantages – provable speedup on reflow operations, proper concurrent editing, elimination of state-corruption bugs – must be accessible without requiring conversion.

This isn't architectural fussiness. It's the difference between a system that proves functional programming solves these problems and one that merely claims it whilst forcing everyone through the same narrow gate.

Consider the alternative: most cross-language projects either compromise the core's purity to make external access easier, or maintain purity whilst making extension nearly impossible. Both approaches fail – the first produces unreliable systems, the second produces unused ones.

First-Class Citizenship

​When I say plugins are first-class citizens, I mean it precisely:
  • Full API access: Plugins can do anything internal code can do
  • No performance penalty: JVM interop means native speed across boundaries  
  • Equal capabilities: The implementation language determines developer experience, not power
  • Designed from the start: The architecture was built with plugins as equals, not added later

Your Java plugin implementing custom layout rules operates with the same capabilities as Clojure core code. The boundary is invisible – just clean interfaces and reliable contracts.
Picture
'Penitenziagite! Penitenziagite!'

No Penitenziagite Here

Ooloi's architecture refuses the mad monk's cry. No demand that you learn Clojure to participate. No leaked functional programming concepts in the public API. No linguistic imperialism masquerading as technical necessity.

The core speaks Clojure because that's the right tool for solving these problems correctly. The plugin system speaks your language because that's the right way to enable participation. Each side of the boundary uses the language appropriate to its purpose.

This is architectural empathy: not compromise, but proper boundary design. The scriptorium can maintain Latin for precision whilst the gate speaks the vernacular.

Picture
Work continues: the monastery's standards hold. The architecture neither shouts incomprehensibly nor demands conversion. There's no Inquisition burning engraving monks at the stake.

The point isn't piety; it's architecture that stays intelligible.

Just clarity, properly structured.

– William of Baskerville

0 Comments

On Feeling like THE Donald

30/9/2025

0 Comments

 
There are days in Ooloi’s development when I feel like the Donald. Not that Donald. Donald Knuth. There's something very real to that comparison, even though it can be seen as presumptuous. Why do I compare myself with a computing giant?

Knuth faced typesetting systems that were brittle, ad hoc, and incapable of scaling to real demands. He didn’t patch; he rebuilt the foundations. Out came deterministic algorithms, the box–glue model, and a system that still sets type decades later.

I’m in a similar place. Music notation software has been compromised for forty years: mutable object graphs, procedural hacks, import/export traps. It works until you open Eine Alpensinfonie or Lontano – then it collapses.

So Ooloi is built the way TeX was:
  • Foundations first. Ooloi has been in development for a year, part-time. No notes have appeared on screen yet, but that isn’t delay, it’s sequence. The first phase was concurrency, traversal, contracts: all the invisible machinery that must be right before visible music can exist.
  • Immutability as discipline. Where TeX used deterministic boxes and glue, Ooloi uses persistent data structures and STM. Both eliminate state leakage as a class of bugs.
  • Correctness as architecture. In TeX, line-breaking is provably optimal. In Ooloi, sharing and traversal are mathematically guaranteed by immutability and functional design.

And even a year isn’t slow, considering what's been implemented in that time. In Clojure, as in Lisps generally, progress is faster, not slower, because the language doesn’t get in the way. Architectural changes that would take months in procedural or OO systems collapse into days when immutability is the default. In Lisps I feel unrestricted from the usual … bullshit.

Durability and Time Horizons

Knuth didn’t set out to make a fashionable typesetter. He built TeX so mathematicians could publish without degradation, and so his own books could be set correctly fifty years later. The result is software still alive in 2025.

That perspective matters. Most software is written to meet the next deadline or release cycle, and dies within five years. Architecture shaped by durability instead of expedience looks very different. It avoids local hacks in favour of structural clarity. It makes changes faster, not slower, because the invariants hold.

Ooloi is built on the same horizon. It’s not about matching today’s competitors feature for feature; it’s about whether the system can handle repertoire and practices that will still be with us in fifty years.

​Knuth wasn't out to childishly 'disrupt' anything. Neither am I. And for the same reasons.

Outlasting Fashion

TeX has been called unfriendly, arcane, even ugly. But it outlasted beautiful GUIs because its correctness was deeper than its interface. It solved the right problem once, and has been binding books and journals ever since.

Ooloi certainly won’t look like TeX. It will be graphical, collaborative, and real-time, and it will have a slick, modern GUI. But it follows the same ethic: stop patching, stop pretending, build a system that doesn’t collapse under its own compromises.

That’s the point of the parallel. Knuth showed what software can be when it’s built for durability rather than fashion. That’s the road Ooloi is on.
Picture
The only Donald that counts
0 Comments

Lisp and the Transcendental

27/9/2025

0 Comments

 
Picture
​I'm one of the world's most committed anti-religious people. Despite decades at organ consoles in churches and cathedrals, I stand with Hitchens: religion is humanity's adolescent phase, something we need to outgrow. Its influence is fundamentally harmful.

But when I read something like How Lisp Became God's Own Programming Language, I completely understand the reverence the author describes. There's something about Lisp – and Clojure – that creates what you can only call a transcendental response. Nothing actually transcendental happens, of course, but the feeling is real.

What Lisp gives you is freedom. I've written about 'windsurfing through parentheses' before, and the metaphor sticks because it captures something essential. Most programmers are chained to the oars of enterprise slave galleys, with CTOs yelling 'RAMMING SPEED!' like that brilliant scene from Ben-Hur. Meanwhile, those of us who've found Lisp are windsurfing in circles around them, enjoying a freedom they can barely imagine.
The discovery feels like Dave Bowman meeting the monolith: 'My God... it's full of stars!' That vertigo when you realise this thing's inner dimensions vastly exceed its outer ones. Lisp isn't transcendental, but it works like a star gate in both senses. The language doesn't get in your way, and it opens new ways of thinking. At the same time, it's so simple that complexity becomes manageable.

I remember that August 1979 BYTE magazine perfectly. The cover promised mysteries, the articles delivered. I couldn't wait to start implementing what they described – eventually doing it in 6502 assembler, using an assembler I'd written in BASIC.

Everything clicked, even as a teenager. This was real freedom, expressed as code.

Years later, I wrote HotLisp (or 'HotLips' – M.A.S.H. was huge then) for the Royal College of Music in Stockholm. It was incredibly ambitious: a full Common Lisp that treated MIDI events as first-class citizens. Looking back, I see this as the beginning of what became Igor Engraver – integrating music directly into the computational core. We used it to control our Synclavier and MIDI synths whilst teaching algorithmic composition to advanced students at the Royal Academy.

The Two-Bit History article nails something important about Lisp's mystique. It traces the evolution from McCarthy's 'elegant mathematical system' through AI research, Lisp machines, and SICP's role in making it the language that 'teaches you programming's hidden secrets'. Each phase built the reputation.

What the article doesn't cover is the educational betrayal that followed. Computer science departments got it right for a while – they taught Scheme as a first language because it let students focus on learning algorithms rather than wrestling with syntax. Pure freedom to think about problems. Then Java Enterprise was foisted upon the world, the departments caved in, and they started churning out galley slaves instead of computer scientists. I see this as nothing short of high treason.

But here's what really matters: that freedom has evolved in Clojure. Rich Hickey didn't just bring Lisp to the JVM – he solved problems that even Common Lisp couldn't handle elegantly. Those immutable data structures aren't academic toys; they're game changers that eliminate whole categories of bugs whilst making concurrency and parallelism natural instead of terrifying. The effects ripple out: undo/redo becomes trivial, and the JVM gives genuine multi-platform reach.

This isn't just improvement – it's architectural breakthrough disguised as evolution. Clojure keeps Lisp's essential quality (that feeling of discovering how programming should work) whilst solving modern problems McCarthy couldn't have anticipated.

The poor souls in corporate Java shops keep rowing, occasionally granted small mercies as functional concepts trickle in – hints of the freedom they're missing.

I wish they could experience what we know: programming doesn't have to feel like industrial labour. There's a way of working where ideas flow directly into code, where the language becomes transparent, where you stop fighting tools and start windsurfing through solutions.

Maybe that's the point. As McCarthy noted in 1980, Lisp survives not because programmers grudgingly accept it as the best tool for each job, but because it hits 'some kind of local optimum in programming language space'. It endures even though most programmers never touch it, sustained by reports from those who've experienced its particular form of computational enlightenment.

Until we can imagine God creating the world with some newer language – and I doubt that day is coming soon – Lisp isn't going anywhere.

Read the full article at Two-Bit History: https://twobithistory.org/2018/10/14/lisp.html
Picture
0 Comments

Hash-Consing

25/9/2025

0 Comments

 
Picture
Musical scores are full of repetition. In a symphony, middle C can appear thousands of times, quarter notes dominate, and the same staccato mark is scattered across every instrument. Most notation software allocates a separate object for each of these occurrences. That means thousands of identical objects, all taking memory and I/O bandwidth for no reason.

Ooloi doesn't.

With ADR-0029, we have implemented selective hash-consing: identical immutable musical objects are represented by the same instance. The same C4, the same staccato, the same quarter note: one object, shared system-wide.

Why "Selective"?

Not everything benefits. A forte marking may appear only a handful of times; a slur always connects specific endpoints. Caching those adds overhead without real gain. So the system targets high-frequency cases (pitches, rests, chords, common articulations) and ignores the rest.

What this changes

  • Memory footprint collapses: a score that would allocate hundreds of thousands of objects now reuses a fraction of them.
  • File sizes shrink: less redundancy on disk and across the network.
  • I/O accelerates: save/load no longer bottleneck on repeated data.
  • GC pressure drops: fewer allocations, less churn.
  • Scalability improves: orchestral and contemporary scores fit comfortably in memory.

All of this is transparent. Musicians won't 'use' hash-consing; they'll just notice that large works open, scroll, and save without drama.

Why it works here

In mutable architectures, shared objects are a trap: one stray modification contaminates every reference. Defensive copying and locks erase any performance benefit. In Ooloi, immutability is the rule. Sharing is safe. No copies, no locks, no bugs.

This isn't the kind of feature that makes a demo screenshot. It's one of the architectural foundations that decides whether the system will still perform when you open Mahler's 8th or La terre est une homme.

It took days to implement. That's the difference Clojure makes.
Picture
0 Comments

The Rendering Pipeline: Ooloi's Core Architecture

23/9/2025

0 Comments

 
Picture
Finishing the statistics infrastructure naturally led to thinking about the next architectural milestone: the rendering pipeline. This is the mechanism that determines what happens when someone clicks into a score to add a note, and how that change propagates through the system.

The design is complete. That in itself is an important milestone, as this is the very foundation on which Ooloi's performance ultimately depends. Everything hinges upon it.

Why a Pipeline?

Picture
Traditional notation software recalculates entire scores when a single element changes. Dense passages in works like Strauss's Elektra bring systems to a halt because every operation is sequential and single-threaded. The reason for this is that parallelism is very difficult to do with mutable state, which is the traditional approach. Scalability often becomes an issue, with diminishing returns as a result.

Ooloi takes the opposite approach and chooses the Clojure way instead, with immutable state. With this, it is comparatively easy to distribute formatting work across all available CPU cores and use them fully and linearly.

Every user action – whether adding a single note or adjusting spacing – thus becomes part of a coordinated cascade where each stage can run in true parallel across all available cores.
​
The goal is straightforward: responsive editing even in the heaviest repertoire.

​Five Stages

ADR-0028 specifies the pipeline in five stages, separating connecting from non-connecting elements and applying a clear fan-out/fan-in pattern.
  1. Minimum Widths (Fan-out): Each measure independently calculates spatial requirements – collision boundaries only. Results are immutable and persist until the content changes.
  2. Vertical Raster (Fan-in): The MeasureStackFormatter collects widths, generates result rasters, and records both minimum and ideal widths. This prepares the ground for spacing optimisation.
  3. Non-Connecting Elements (Fan-out): Independent symbols – noteheads, accidentals, articulations, dynamics – are prepared in parallel. They don't need finalised atom positions.
  4. Discomfort Minimisation: Iterative optimisation adjusts widths and system breaks to reduce proportional distortion. No layout changes occur after this stage. From here on, positions are fixed.
  5. Connecting Elements​: Slurs, ties, beams, hairpins, and glissandi are drawn only once final positions are known. Sequential coordination ensures continuity across measures and systems.

This separation allows Ooloi to exploit parallelism where possible and enforce order where necessary.

​Plugins as First-Class Citizens

Picture
Formatting in Ooloi is plugin-driven. Core elements such as notes and beams are implemented through the same interfaces available to extensions.
​
Plugins can participate in different stages depending on their needs:
  • Spacing hook (Stage 1) for collision boundaries
  • Paint hook (Stage 3) for independent elements
  • Connect hook (Stage 5) for elements spanning multiple atoms

Simple articulations may use only the first two; beams may require all three. This uniform model ensures extensibility without compromising performance.

Convergence by Discomfort

The optimisation engine measures deviation from ideal proportions across measures, systems, and pages. Improvements multiply: small gains in multiple places compound into significant overall reductions. Hard constraints such as manual breaks provide natural stopping points.
​
This replaces arbitrary iteration limits with a principled measure of quality.

​Parallelism and Responsiveness

Claypoole provides efficient thread-pool execution, delivering significant speed-ups over built-in Clojure parallelism. STM transactions keep operations atomic while allowing concurrency inside each stage. Cooperative cancellation ensures that rapid user input remains responsive.
​
The system treats a single user as a 'collaboration network of one'. The same infrastructure that supports multi-user editing ensures smooth interaction for individuals.

​Where This Leads

Picture
This pipeline is the structural core that should make scrolling through Elektra or Ligeti's Requiem as fluid as editing a Gnossienne by Satie.
​
The specification is complete. Implementation begins as soon as the current phase closes. Ooloi's promise of responsive, professional-scale notation depends on it.

​Scary stuff.


​Full specification: ADR-0028: Hierarchical Rendering Pipeline with Plugin-Based Formatters
0 Comments

Ooloi Server Architecture Documentation

22/8/2025

0 Comments

 
Picture
I've just published the Ooloi Server Architectural Guide documenting the backend implementation and its characteristics.

The server combines Clojure's STM with gRPC for concurrent access patterns, uses a unified protocol design to eliminate schema complexity, and integrates real-time event streaming for collaborative editing.

​The guide covers the architecture, technical implementation, performance characteristics, and deployment scenarios for anyone interested in the server architecture details.

And now, back to the frontend client implementation...
0 Comments

Why I Left Clojurians

18/8/2025

2 Comments

 
Picture
Every community has its breaking point. Mine came on Clojurians when I wrote a single sentence:

'Clj-kondo can go away – I have 18,000 tests'.

That was enough to get my post deleted. Before the deletion, there was 'discussion' – if you can call it that. I was told my statement was nothing more than click bait.

The irony? The author of clj-kondo himself agreed with me.

What That Line Meant

It wasn't click bait. It was a statement of principle:
  • Tests prove correctness. They're executable, falsifiable, and domain-driven.
  • Linters and static analysis don't. They enforce style, not truth.
  • Dynamic dispatch makes static analysis less meaningful. Ooloi's architecture relies heavily on Methodical multimethods and polymorphism throughout. Static analysis tools can't trace through runtime polymorphic calls, making their warnings less informative than executable tests that actually exercise these dynamic paths.
  • When you've got 18,000 tests running clean, you don't need a priesthood of external validators telling you your code is 'unsafe'.

And I was careful to make the distinction explicit: clj-kondo is a beloved, useful tool. For most projects it adds value. It just happens to be of limited use in my project, because Ooloi's architecture is already validated at a different scale.

That nuance – acknowledging the tool's value whilst drawing boundaries around its relevance – should have been the beginning of a sober technical discussion. Instead, it was treated as provocation. The fairness itself was read as heresy.

The Culture Clash

The moderator (a 'Veteran Architect') didn't engage with the point. He reacted from the gut: pearl-clutching, dismissing, and finally deleting. Exactly the kind of gatekeeping I dissected in my article on functional programming gatekeeping.

And let me be clear: I have nothing against the Clojurians themselves. They're a knowledgeable, interested lot, often deeply engaged in technical detail. The problem isn't the community – it's the moderation culture.

The moderators behave more like a church council than facilitators of discussion. Their first instinct isn't to sharpen an argument, but to protect orthodoxy, maintain decorum, and suppress anything unsettling.

The ideal they enforce seems to be some kind of cold, robotic detachment – the lab-coat fantasy of neutrality – or perhaps the modern American obsession with never offending anyone, no matter how bloodless the discourse becomes. Either way, it enforces sterility, not clarity.

You can critique syntax sugar all day long, but question a community darling like clj-kondo – even whilst calling it useful and respected – and suddenly you're accused of trolling.

Why I Left

I didn't leave because I was offended. I left because I refuse to participate in a space allergic to honesty. If a community sees a blunt critique and immediately cries click bait – ignoring both the nuance of my post and the fact that the tool's own author agreed – it has no business in my world.

Ooloi is built on clarity, not ceremony. It's an architecture tested by 18,000 executable truths, not validated by a linter's opinion. If that treads on toes, good. Prissy people afraid of dark humour or communication nuances that wouldn't pass muster at a parish council don't belong in this project. And the same thing goes for hypocrites who say, 'We're inclusive here - as long as you're exactly like us'.

The Broader Lesson

Communities often confuse politeness for health. But real progress requires the courage to tolerate discomfort. If you need your software conversations padded with pillows, you'll never survive the weight of real architecture.

As Wednesday Addams would remind us: hypocrisy is uglier than bluntness, and dishonesty is far more offensive than a glass of gin before noon. Or, indeed, a well-placed 'fuck you'.

So I deleted my Clojurians account. Because sometimes subtraction is progress.

Picture
2 Comments

You Fucking Moron: How to Collaborate with AI Without Losing the Plot

20/7/2025

0 Comments

 
Picture
After a year building the backend of Ooloi with Claude, I’ve learned this:

Successful AI collaboration isn’t about creative freedom. It’s about harsh constraint.

AI will overstep. Your job is to correct it—immediately, uncompromisingly.
The friction isn’t failure. It’s the method.

Read the full piece – which I asked the AI to write in its own voice – here.
0 Comments

I Couldn't Wait Any Longer: Complete Ooloi Documentation Released

6/7/2025

0 Comments

 
Picture
​I've been saying I'm itching to open source Ooloi, but waiting for the complete software felt increasingly artificial. The documentation tells the real story – twenty-five years of architectural evolution from Igor Engraver through AWS systems thinking to this closure in Clojure.

So here it is: the complete Ooloi documentation collection, released publicly ahead of the software itself.
What you'll find:
  • Comprehensive guides teaching advanced Clojure through musical problems
  • Complete architectural decision records showing our thinking
  • The timewalker guide (with three Rocky Horror references, naturally)
  • Real functional programming education through meaningful domain examples

This represents my attempt to walk the talk after critiquing FP drawbridge syndrome. These guides serve dual purpose: they document sophisticated music notation architecture whilst teaching functional programming concepts through examples that actually matter.

The timewalker guide alone demonstrates transducers, lazy sequences, and functional composition through orchestral score traversal. The polymorphic API guide teaches multimethods through musical type systems. The concurrency patterns show STM coordination through collaborative editing.

Twenty-five years of thinking, distilled into something that I hope proves useful beyond music software. The architecture is complete; the foundations are solid. Now you can see why I've been eager to share this work.

An organism evolved, indeed.
​
Explore the complete documentation: https://github.com/PeterBengtson/Ooloi-docs

​/Peter
0 Comments

The Architecture Behind Ooloi: A Quarter-Century in the Making

4/7/2025

0 Comments

 
Picture
Now that the backend engine is complete, architecturally speaking, I'm getting increasingly eager to open the source. Itching, in fact. But as that will take a while, the least I can do for you is give you the WELCOME.md file from the source repo. 

​/ Peter

Welcome to Ooloi

Greetings, and welcome to Ooloi, the spiritual successor to Igor Engraver. If you're seeking yet another conventional music notation software, I'm afraid you've taken a wrong turn. Ooloi aims to be something rather different — and there's a story behind why that matters.

A Quarter-Century in the Making

Twenty-five years ago, I created Igor Engraver, which became rather successful in the music notation world. When that project ended, it left something unfinished – not just the software, but the understanding of what music notation software could truly become. Ooloi represents the completion of that circle, built with decades of accumulated insight about both music and programming.

In the intervening years, I became an AWS Solutions Architect Professional and created systems like Ocean and OpenSecOps. I have always thought in systems — this shift simply allowed me to give that instinct full rein, to focus entirely on designing foundations that can handle complexity and scale over time through elegant abstraction layers.

I've spent the better part of a year on Ooloi distilling everything I've learned into an architecture that doesn't just work, but works elegantly. This isn't my first attempt at solving these problems, but it's the first time I've had the right tools – functional programming, immutable data structures, enterprise-scale systems thinking, and the kind of patience that comes with experience – to solve them properly.

What is Ooloi?

Ooloi is open-source music notation software, designed from the ground up to handle complex musical scores with both finesse and power. Built in Clojure, it represents a fundamental rethinking of how music software should work.

What makes it different:
  • Temporal coordination: A traversal system that understands musical time
  • Collaborative by design: Multiple musicians can edit the same score simultaneously without conflicts
  • Memory efficient: Handles massive orchestral scores without breaking a sweat
  • Extensible architecture: A lean core augmented by plugins in any JVM language
  • Professional output: High-quality rendering and printing that works identically across platforms
  • Cross-platform: Mac OS X, Windows, Linux

Why Ooloi Matters

The world of music notation software has been rather stagnant for too long, content with incremental updates and feature bloat. Most existing software suffers from fundamental architectural problems that can't be fixed with patches – they require starting over with better foundations.

Ooloi solves problems that have plagued music software for decades: proper temporal synchronization, efficient collaborative editing, memory-efficient handling of large scores, and clean extensibility. These aren't just nice features – they're qualitatively different capabilities enabled by choosing the right abstractions.

The Architecture You'll Inherit

What you'll find here is the result of taking time to get the abstractions right. The backend is conceptually complete, with over 15,000 tests proving it works correctly. The temporal coordination system, the pure tree data structures, the STM-based concurrency – these represent solutions to genuinely hard problems.

But here's the thing: good architecture should be invisible to those who use it. The complexity is handled for you, hidden behind interfaces that make difficult things simple. You can focus on the problems you want to solve – whether that's creating plugins, improving the user interface, or adding new musical capabilities.

How You Can Contribute

If you're here, you probably have an interest in music, programming, or ideally both. Here's how you can be part of this:
  1. Core Development: Help improve core functionality or add new features. Clojure experience is valuable, but the architecture is designed to be learnable.
  2. Plugin Development: Create plugins to extend Ooloi's capabilities. The plugin system supports any JVM language — Java, Kotlin, Scala, even proprietary commercial plugins.
  3. Documentation: Help make complex concepts accessible. The goal is clarity without dumbing down.
  4. User Experience: Contribute to interface design. The aim is intuitive interaction that serves creative work.
  5. Testing: Help ensure reliability. With this level of architectural complexity, comprehensive testing isn't optional.
  6. Ideas and Vision: Share your thoughts on how we can improve. Constructive feedback shapes the future.

Getting Started

  1. Read our Code of Conduct and Contribution Guidelines. They set the tone for how we work together.
  2. Start with the backend in the backend/ directory. That's where the architectural foundations live.
  3. Explore the documentation, especially the ADRs (Architecture Decision Records) that explain why certain choices were made.
  4. Use the REPL extensively. Clojure's interactive development environment is powerful for understanding how the system works.
  5. Review open issues or create new ones. The architecture is solid, but there's always more to build.

What You're Joining

This isn't just another open-source project. It's the culmination of decades of understanding what music notation software needs to be, combined with the architectural discipline to build it right.

You're joining something that's designed to outlast its creator, to enable work that hasn't been imagined yet, to solve problems that matter to musicians and developers alike. The foundations are solid; now we build the future on top of them.

The architecture is complete, but the work is just beginning. There are plugins to write, interfaces to design, capabilities to add. Most importantly, there are problems to solve that only emerge when you put powerful tools in the hands of creative people.

A Personal Note

At 64, carrying more than five decades of programming experience and a parallel career as a composer, I've tried to encode into this architecture not just technical solutions, but the aesthetic judgments and performance intuitions that come from actually making music.

The creative energy that might have gone into another opera has found expression in software architecture. It's a different kind of composition – one that enables other people's creative work rather than expressing my own. In many ways, it's more satisfying.

This is what happens when you take the time to get it right, when you resist the urge to rush, when you're willing to solve the hard problems properly. The result is something that can grow and evolve through the contributions of others while maintaining its essential character.

Now, let's make some music. On all levels.

/ Peter Bengtson
0 Comments

The Musical Journey to Understanding Transducers: Building Ooloi's Piece-Walker

6/6/2025

0 Comments

 
Picture
How solving a real music notation problem revealed the perfect transducer use case

​The Problem That Started It All

I found myself confronting what appeared to be a deceptively simple requirement for Ooloi: 'Resolve slur endpoints across the musical structure'.

Rather straightforward, one might assume: simply traverse the musical structure and locate where slurs terminate. But then, as so often is the case, the requirements revealed added complexity:
  • Slur endpoints must be discovered in temporal order (measure 1 before measure 2, naturally)
  • Yet slurs can span multiple voices on the same staff
  • Or cross between neighbouring staves within an instrument
  • And precisely the same temporal traversal logic would be required for MIDI playback
  • Not to mention visual layout calculations and formatting
  • And harmonic analysis
  • And collaborative editing conflict resolution
Picture
It became apparent that I needed a general-purpose piece traversal utility: something handling temporal coordination whilst remaining flexible enough for multiple applications. Rather than construct something bespoke (and likely regrettable), I researched the available approaches within Clojure's ecosystem.
​
That's when I recognised this as precisely what transducers were designed for.

​The Architecture Recognition

​Allow me to demonstrate the pattern I anticipated avoiding. Without a general traversal utility, each application would require its own approach:
Picture
Three functions, identical traversal logic, different transformations. Exactly the architectural smell I wanted to avoid from the outset.
​
This was precisely Rich Hickey's transducer insight made manifest: "What if the transformation was separate from the collection?"

​The Transducer Revelation

​What if I could write the temporal traversal once, then apply different transformations to it?
Picture
Objective achieved: one traversal algorithm, many applications.
​
But its architectural reach turned out to be even more profound.

​The Architectural Insight

The design decision hinged upon recognising that I was conflating two distinct concerns: the mechanism of traversal and the logic of transformation. This wasn't merely about avoiding the tedium of duplicated code (though that would have been reason enough) but rather about establishing clean architectural boundaries that would serve the system's long-term evolution.

Consider the conceptual shift this separation enabled:
​
Rather than thinking in terms of specific operations upon musical structures:
  • 'I need to find slur endpoints in this piece'
  • 'I need to generate MIDI from this piece'
  • 'I need to calculate layout from this piece'

The transducer approach encouraged thinking in terms of composed processes:
  • 'I need to traverse this piece temporally, then apply endpoint filtering'
  • 'I need to traverse this piece temporally, then apply MIDI transformation'
  • 'I need to traverse this piece temporally, then apply layout transformation'

The traversal thus became reusable infrastructure, whilst the transformation became pluggable logic. This distinction would prove invaluable as the system's requirements expanded.

​The Broader Applications

​What I hadn't anticipated was how broadly applicable the resulting abstraction would prove. After implementing the piece-walker for attachment resolution, I discovered it elegantly supported patterns I hadn't originally considered, each demonstrating the composability that emerges naturally from separating traversal concerns:
Picture
​Each is built from simple, testable pieces. And they all inherit the same temporal coordination guarantee. This composability emerged naturally from the transducer design: a pleasant architectural bonus.

​The Performance Characteristics

As one would expect from a well-designed transducer, memory usage remained constant regardless of piece size: a particularly crucial consideration when dealing with the sort of orchestral scores that might contain hundreds of thousands of musical elements.
​
Consider the alternative approach, which would create intermediate collections at each processing step:
Picture
​The transducer version processes one item at a time:
Picture
​Same result, constant memory usage. This exemplifies what Rich meant by 'performance without compromising composability'.

​Demystifying Transducers

Transducers suffer from an unfortunate reputation for complexity, often relegated to 'advanced topics' when they needn't be. This is particularly galling given that they're fundamentally straightforward when you encounter the right use case, which the musical domain provides in abundance.
​
Think of transducers as 'transformation pipelines' that work with any data source, much as one might design AWS data processing workflows that operate regardless of whether the data arrives from S3 buckets, database queries, or API streams:
Picture
The pipeline stays the same. The data source changes.
​
In Ooloi:
Picture

​Why This Matters Beyond Music

The piece-walker solved a universal software problem: How does one avoid duplicating traversal logic whilst maintaining performance and composability?

This pattern applies everywhere:
  • Web scraping: Same page traversal, different data extraction
  • Log analysis: Same file reading, different filtering and aggregation
  • Database processing: Same query execution, different transformations
  • Image processing: Same pixel iteration, different filters

Transducers provide the infrastructure for "traverse once, transform many ways."

​The Bigger Picture

Building the piece-walker demonstrated that transducers aren't an abstract functional programming concept. They're a practical design pattern for a specific architectural problem: separating the concerns of traversal from transformation.

The musical domain made this separation particularly clear because the temporal coordination requirements are so explicit. When you need the same traversal logic applied with different transformations, transducers provide the elegant answer.
​
This separation makes code:
  1. More testable (test traversal and transformations independently)
  2. More reusable (same traversal, different applications)
  3. More maintainable (one place to optimise traversal performance)
  4. More composable (mix and match transformations)

What's Next?

The piece-walker is documented thoroughly in our Architecture Decision Record for those wanting technical details. But the real value lies not in the musical specifics but in observing how transducers address genuine architectural challenges with apparent effortlessness.

The next time you find yourself contemplating similar data processing logic across multiple contexts, you might ask: 'What if the transformation was separate from the collection?'
​
You may well recognise your own perfectly suitable transducer use case.

References and Further Reading

Rich Hickey's Essential Talks
  • "Transducers" - Strange Loop 2014 - The definitive introduction to transducers by their creator. This talk explains the core concepts, motivation, and design philosophy behind transducers.
  • "Inside Transducers" - Clojure/conj 2014 - A deeper technical dive into the implementation details of transducers, focusing on the internals and integration with core.async.

Official Documentation
  • Clojure Reference: Transducers - The official Clojure documentation provides comprehensive coverage of transducer usage, with examples and best practices.
  • ClojureDocs: transduce - Community-driven documentation with practical examples of the transduce function.

Educational Resources
  • "Can someone explain Clojure Transducers to me in Simple terms?" - Stack Overflow - An excellent community discussion breaking down transducers for beginners.
  • "Grokking Clojure transducers" - /dev/solita - A comprehensive tutorial that builds intuition for transducers through examples.
  • "What if… we were taught transducers first?" - Clojure Civitas - An interesting pedagogical perspective arguing for teaching transducers before lazy sequences.
  • "Using Clojure Transducers: A Practical Guide" - FreshCode - A practical guide with real-world examples and performance considerations.

Advanced Topics
  • "Reducers, transducers and core.async in Clojure" - Eli Bendersky - Explores the evolution from reducers to transducers and their integration with concurrent programming.
  • Rich Hickey Talk Transcripts - Complete transcripts of Rich Hickey's talks, including both transducer presentations, for detailed study.
0 Comments

Functional Programming: Beyond the Vampire Castle

29/4/2025

0 Comments

 
Picture
I recently spotted a meme on LinkedIn featuring a brooding, aristocratic Dracula asking, "What is a monad but a monoid in the category of endofunctors?" – accompanied by no explanation whatsoever. I chuckled, as one does when recognising an in-joke, but then found myself pondering a rather uncomfortable question: Is this really how we want to present functional programming to the world?

The Allure of the Arcane

There's something undeniably satisfying about mastering difficult concepts. When I first grasped the elegant power of immutable data structures, higher-order functions, and composability, it felt like discovering a secret door in a familiar house – one that led to a vast, beautiful landscape I'd never known existed. That eureka moment is profound, even transformative.

But here's the rub: why do we in the functional programming community so often present that door as if it requires an arcane ritual to unlock? Why do we seem to relish the impenetrability of our terminology?

Let's be honest with ourselves. There's a whiff of intellectual snobbery in the air when functional programmers gather. We've all heard (or perhaps even made) the disparaging remarks about "Java drones" or "code monkeys churning out mutable state on corporate slave galleys". We swap stories about the moment we "finally understood monads" as if recounting our initiation into a secret society. We wear our hard-won knowledge as a badge of honour – and sometimes, if we're being truly honest, as a mark of superiority.

The Hidden Cost of Exclusivity

This exclusivity comes at a cost. Functional programming remains woefully underutilised in the industry at large, despite offering compelling solutions to many of the problems that plague software development. Concurrency, side effect management, robust testing, code reuse – these aren't niche concerns, they're central challenges in modern software engineering. Yet we've somehow managed to position our toolbox of solutions as if it's too rarified for everyday use.

Consider the meme I mentioned. For the initiated, it's a humorous reference to a famously opaque definition. For everyone else, it's not just impenetrable – it actively signals that they don't belong in the conversation. It's a velvet rope strung across the entrance to a club they didn't even know they might want to join.

I'm reminded of my journey from Common Lisp to Clojure. Both languages offer powerful functional paradigms, but Clojure has managed to achieve something remarkable: it has brought functional programming concepts to a significantly wider audience. It didn't accomplish this by watering down the functional paradigm, but by emphasising practicality alongside purity. By meeting developers where they are – on the JVM, with access to the libraries and tools they already know – Clojure created an on-ramp rather than a barrier.

No Monads Required

Here's something we don't acknowledge often enough: you don't actually need to understand monads to be a productive functional programmer. In fact, in languages like Clojure, you can write elegant, powerful functional code for years without ever encountering the term. The core principles of functional programming – immutability, pure functions, higher-order functions, composability – can be understood through practical examples without diving into category theory.

This isn't to say that the deeper theory isn't valuable. Of course it is! The mathematical foundations of functional programming provide extraordinary insight and power. But they're not the entrance exam. They're advanced courses you can take once you're already enrolled.

Yet somehow, we've allowed the most theoretically complex aspects of functional programming to become its public face. It's as if we're advertising a car based on its differential equations rather than where it can take you.

The False Dichotomy

There's an insidious implication lurking beneath this state of affairs: the notion that there are two classes of programmers – the enlightened few who grasp the elegant abstractions of functional programming, and the unwashed masses writing imperative code. This is, to put it bluntly, utter bollocks.

The reality is far more nuanced. Most programmers exist on a spectrum of understanding and application of functional principles. Many Java developers make excellent use of streams and lambdas. JavaScript programmers increasingly embrace immutability and pure functions. Even in traditionally imperative contexts, functional patterns are gaining traction.

These aren't "lesser" applications of functional programming – they're pragmatic adaptations that solve real problems. The senior developer who introduces Option types to avoid null pointer exceptions in a legacy Java codebase is applying functional thinking in a way that delivers immediate, tangible benefits. Should we really consider them less enlightened than someone who can recite the monad laws but has never shipped a product?

Building Bridges, Not Walls

If we truly believe in the benefits of functional programming – and I certainly do – then we should be building bridges to make these ideas more accessible, not walls to keep the uninitiated out.
​
What might this look like in practice?

For starters, we could focus our evangelical efforts on the practical benefits rather than the theoretical foundations. When I'm talking to a team about adopting Clojure or functional patterns, I don't open with category theory. I talk about dramatically reduced bug rates due to immutability. I show how composable functions lead to more reusable code. I demonstrate how side effect isolation makes testing simpler and more reliable. And to really wow them, I might describe how immutable data structures instantly solve the Undo/Redo problem for any application with a frontend.

We could also acknowledge that functional programming isn't an all-or-nothing proposition. Encouraging teams to adopt functional patterns incrementally within their existing codebases can yield substantial benefits and build confidence for deeper adoption. The choice isn't between pure Haskell and imperative spaghetti code – there's a vast, productive middle ground.

Most importantly, we need to shift our community culture away from exclusivity and toward inclusivity. This doesn't mean abandoning rigour or depth – it means recognising that everyone starts somewhere, and the journey from imperative to functional thinking is challenging enough without adding artificial barriers.

The Clojure Case Study

It's worth examining why Clojure has been relatively successful in attracting developers who might otherwise have been put off by functional programming's reputation for difficulty.

Firstly, Clojure is pragmatic to its core. It embraces functional principles without dogmatism, allowing for controlled mutability when necessary. It prioritises solving real problems over theoretical purity.

Secondly, it meets developers where they are. By running on the JVM (and later JavaScript engines), it allows gradual adoption and interoperability with existing systems.

Thirdly, and perhaps most importantly, the Clojure community has generally focused on what the language enables rather than how clever you need to be to use it. The emphasis has been on what you can build, not on arcane knowledge as a status marker.

This approach hasn't compromised the language's functional credentials – Clojure remains thoroughly functional in its philosophy and implementation. But it has made those functional principles accessible to a much wider audience.

A More Welcoming Future

I'd like to see a functional programming community that celebrates bringing new people in rather than keeping them out. One that takes as much pride in making complex concepts accessible as it does in mastering them.

Imagine if our response to someone struggling with functional concepts wasn't "Well, once you understand monads..." but rather "Here's how this approach solved a similar problem I had..."

Imagine if we shared our enthusiasm not through insider references that exclude the uninitiated, but through concrete examples that demonstrate the real-world power of functional approaches.

Imagine if we recognised that the true measure of our understanding isn't our ability to recite category theory, but our ability to apply these principles to build better software and help others do the same.

As I continue to develop Ooloi, my open-source music notation project in Clojure, I'm constantly reminded of this tension. The functional paradigm allows me to build a system that's both powerful and elegant. The code is concise, composable, and expressive in ways that would be difficult to achieve with imperative approaches. But when I'm documenting the system or preparing it for open-source collaboration, I'm mindful of the need to make these concepts accessible.

The goal isn't to water down the functional aspects – they're essential to the system's design. Rather, it's to provide on-ramps that allow developers to engage with these ideas gradually, to see their practical benefits before diving into the deeper theory.

Conclusion

Functional programming isn't a secret to be jealously guarded – it's a powerful set of tools that can dramatically improve how we build software. Its core insights deserve to be widely shared and applied.

​So the next time you're tempted to share that monad joke or category theory reference, ask yourself: Am I opening a door or closing one? Am I inviting others to share in something valuable, or am I simply signalling my own cleverness?

We don't need gatekeepers in the functional programming community. We need guides. Guides who remember what it was like not to know, who can explain complex concepts in accessible terms, who take pride not in how exclusive their knowledge is but in how effectively they can share it.
​
After all, what is a community but a group of people helping each other grow?

0 Comments

    Author

    Peter Bengtson –
    Cloud architect, Clojure advocate, concert organist, opera composer. Craft over commodity. Still windsurfing through parentheses.

    Search

    Archives

    January 2026
    December 2025
    November 2025
    October 2025
    September 2025
    August 2025
    July 2025
    June 2025
    April 2025
    March 2025
    September 2024
    August 2024
    July 2024

    Categories

    All
    Accidentals
    Alfred Korzybski
    Architecture
    Benchmarks
    Clojure
    CLOS
    Common Lisp
    DDD
    Death Of Igor Engraver
    Documentation
    Donald E Knuth
    Dorico
    Dynamic Programming
    Finale
    FrankenScore
    Franz Kafka
    Frontend
    Functional Programming
    Generative AI
    GRPC
    Igor Engraver
    Jacques Derrida
    JVM
    License
    LilyPond
    Lisp
    Localisation
    MIDI
    MPL 2.0
    MuseScore
    MusicXML
    Ooloi
    Ortography
    Pitches
    Playback
    Plugins
    Python
    QuickDraw GX
    Rendering
    Rhythm
    Rich Hickey
    Road Map
    Scheme
    Semiotics
    Sibelius
    Site
    Skia
    Sponsorship
    UI
    Umberto Eco
    Vertigo
    VST/AU
    Wednesday Addams

    RSS Feed

Home
​Overview
Documentation
About
Contact
Newsletter
Ooloi is an open-source desktop music notation system for musicians who need stable, precise engraving and the freedom to notate complex music without workarounds. Scores and parts are handled consistently, remain responsive at scale, and support collaborative work without semantic compromise. They are not tied to proprietary formats or licensing.
​
Ooloi is currently under development. No release date has been announced.


  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact
  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact