|
I'm writing this from a mountainside near Palermo on Easter Eve, the day the liturgical calendar leaves empty. Yesterday was the Crucifixion. Tomorrow is the Resurrection. Today is the silence between them; the day when, if you take the story seriously, God is dead and nobody yet knows what comes next. Yesterday, one of my closest friends hanged himself. He is now unconscious on a ventilator. Nobody knows why he did it. Nobody knows what comes next for him either. I'm not a believer. I've never been a believer. But I am an organist, and I've participated more times than I can count in that piece of symbolic theatre we call the liturgy. I have watched the Easter arc – from Maundy Thursday through the desolation of Good Friday to the silence of Saturday and the blazing major key of Sunday morning (Richard Strauss would of course have set it as a C major 6/4 chord) – move people in ways that have nothing to do with whether the story is true. The psychological architecture of it is real, even if its subject is not. I understand Easter Saturday better today than I did yesterday, and not because I've found faith, but because I'm living in the structure. So I sit on a terrace in Sicily with a stupendous view and seven people I'm fond of, and the light is extraordinary, and a friend is suspended between life and death 2500 kilometres away, and there's nothing I can do. Which makes it a reasonable day to take stock. We went to the Teatro Massimo two days ago. It's the third largest opera house in Europe, and the building itself is magnificent: the kind of architecture that earns the word. What happened on stage, however, was something else. Don Quichotte. Classical ballet. Minkus. Classical ballet of this type is a closed system of circular reasoning so thorough that its practitioners no longer notice it. It no longer has anything to say, yet they defend Minkus's oom-pah-oom-pah as though it were art. They define 'musicality' as the ability to dance in time with the beat. A musician would find this primitive in the extreme, and in fact not a description of musicality at all, but a description of mere competence. It's the conflation of floor with ceiling. And yet the system sustains itself, because it has constructed an aesthetic vocabulary that exists only to justify its own continuation. Any external reference point is treated as irrelevant. What remains is religion, not art. I recognise the pattern. I've encountered it in functional programming circles too: articles of faith masking real emptiness, defended not by argument but by social enforcement. The dynamic is identical. The domain is irrelevant. In the Christian calendar, today is the day of unresolved tension, the day when the official story hasn't yet provided its answer. What I find clarifying about this particular Saturday is the absence of dogma. The story hasn't yet told you what to believe. For twenty-four hours, you're left with the facts. I prefer the facts. When people ask why this blog looks the way it does – why there's so much documentation, so many architecture decision records, so much apparent excess – the honest answer again has to do with death. I created Igor Engraver in the mid-1990s because I hated Finale. I had written an entire opera in it and the experience was sufficiently wretched to convince me that the problem was one of fundamental architecture. Igor was a music notation system that was, by most accounts, genuinely ahead of its time. It was acquired by idiots and left to die. What I've come to understand, twenty-five years later, is that almost every fundamental architectural decision in Ooloi is a response to what happened to Igor, whether I originally intended it that way or not. Open source, so that no individual's mismanagement can bury the work. Open-source components throughout, so that no proprietary dependency can become a chokepoint. ADRs. Guides. A RAG system that can answer deep, architectural questions about the codebase. A public blog that documents development as it happens, including the false starts and course corrections. This is not a vanity exercise in transparency. It's an insurance policy. If I'm hit by a bus tomorrow, the work survives. Someone can pick it up, read why every decision was made, and continue. The documentation isn't supplementary to the project. It's structural. It's the specification. Igor died and nothing survived it. I'm sixty-four. Therefore the work must be legible without its author. I never discuss other notation programs on this blog, and people occasionally find this odd. The reason isn't diplomatic restraint; it's that I genuinely don't think about Ooloi in competitive terms. Ooloi exists because I'm convinced there's a better way to represent music computationally, and I believe I've found a significant part of it, and I want to build it properly and make it available, so that a field that's been stagnant for forty years has a reason to move. That is the entirety of the motivation. What I am doing inside Ooloi is architecturally different. And that's why I've named it after a third-gender space alien. It's a categorically different thing, and it needs to be. The technical argument for why is elsewhere on this blog, at length, and I won't repeat it here. What matters today is that the work exists, that it's open, and that it's legible. Because today is a day for thinking about what survives. What connects the friend on the ventilator, the ballet at the Teatro Massimo, the small dogmas of programming communities, and the theological void of Easter Saturday – apart from fragility, ossification, and death – is perhaps nothing more than this: that I have no patience for systems of thought that protect themselves from examination. Religious faith (which is wishful thinking fanned by fear of death) does this by design; classical ballet and programming communities accomplish the same thing through ossified aesthetic insularity and social enforcement respectively, but the mechanism is identical. All of them ask you to treat the absence of external validation as evidence of self-sufficiency rather than what it actually is: a warning.
Tomorrow is Easter. The faithful will celebrate a resurrection I don't believe in. My friend may wake up, or he may not. The view from this mountain is unchanged. Sicily doesn't care about any of it. That, too, is a kind of honesty.
5 Comments
That room. The tape drives in their cabinets along the far wall, the operator's console in the centre, the line printer to the right. It looks like something from Kubrick: the same antiseptic geometry, the same monolithic seriousness, the same implication that whatever happens here is important enough to require its own architecture. This is a Philips P1100. In 1973, I was twelve years old, and this is where I started programming. I got the opportunity through school. The P1100 was a Dutch mainframe, not widely known outside Northern Europe, and the fact that a twelve-year-old was permitted to approach it probably says more about 1970s Scandinavian attitudes to children than about any pedagogical intention. But approach it I did. COBOL and FORTRAN, mostly. Punch cards. Line printers. Typewriter console input. Thirty-six-bit words and 32K words of core memory – actual magnetic cores, the kind you could theoretically see with a magnifying glass. Spinning tape stations and disk units. The whole physical apparatus of computation as it existed before anyone thought to make it personal. What the photographs convey, and what no description quite captures, is the physicality of early computing. These were rooms, not screens. You walked into the machine. The tape drives were furniture. The noise was constant. Programming in that environment carried a particular weight; there was nothing casual about it, nothing disposable, nothing you could do from bed with a laptop. The seriousness was built into the space. I don't mention this for nostalgia. I mention it because something from that period has stayed with me through fifty years of subsequent work, and I think it matters for understanding why I build the way I build. A Language That Trusts You Six years after the P1100, in 1979, I encountered Lisp. I was eighteen, and the encounter came through the legendary August 1979 issue of BYTE magazine, devoted entirely to Lisp. I've written about this before, so I won't repeat the full story here; but the experience deserves its place in this genealogy because it changed the direction of everything that followed. I should say what computing felt like in the late 1970s, for anyone who wasn't there. The dominant paradigm was procedural: FORTRAN, COBOL, Pascal, various assembly languages depending on your hardware. Programming meant telling the machine what to do, step by step, in a language designed for the machine's convenience as much as the programmer's. The programmer was an operator. The metaphor was industrial. I had spent six years in that world. It was what programming was. Lisp was different in kind, not just in degree. What I encountered was a language that didn't tell me what to do. It waited. It provided a set of primitives so minimal they seemed almost absurd; seven or eight special forms, a way to define functions, a way to combine them, and the extraordinary implication that this was sufficient for anything I could imagine. The language was smaller than what I'd been using, and it could do more. That paradox has never stopped being interesting to me. What Lisp offered, and what I wouldn't have the vocabulary to articulate until years later, was freedom. Not freedom as a marketing term or a political slogan, but freedom as an engineering condition: the ability to reshape the language to the problem rather than reshaping the problem to fit the language. In Lisp, the programmer was not an operator. The programmer was a maker of tools, a builder of languages, a creative agent trusted with the full power of the system. The culture around Lisp carried this same ethos. The people who shaped it (Steele, Gabriel, Kiczales, Norvig, among others) were not priests. They were builders, hackers, improvisers. They valued expressiveness, wit, elegance in the mathematical sense, and above all, the conviction that the person at the keyboard was intelligent enough to be trusted with dangerous tools. The community was irreverent, argumentative, often brilliant, and entirely uninterested in policing anyone's tone or orthodoxy. You were judged by what you built, not by which catechism you recited. I mention these names because they are not abstractions to me. I was eighteen. I was reading their papers. I was trying to implement what they described. Immediately after that BYTE issue, I implemented a Lisp in 6502 assembler, using an assembler I had written in BASIC. I tell you this not to impress but to convey the intensity of the response. When something is that good, you build it yourself to understand it. CLOS and the Workshop TraditionThe progression from basic Lisp to the Common Lisp Object System (CLOS) in the mid-1980s was not, for me, a move from one paradigm to another. It was an expansion within the same tradition. CLOS is difficult to explain to anyone whose experience of object-oriented programming was formed by Java, C++, or Python. In fact, some of us maintained even then that CLOS was not object-oriented at all; the name was a concession to the terminology of the period, not a description of what it actually was. Those languages treat objects as containers: data and methods bundled together, accessed through interfaces, extended through inheritance. The programmer works within the object. CLOS transcends this entirely. Methods don't belong to objects; they belong to generic functions. Dispatch can happen on any combination of argument types. Method combinations (:before, :after, :around) let you layer behaviour without modifying existing code. The Metaobject Protocol (MOP) lets you redefine how the object system itself works. The result is a system of extraordinary power and openness. You could override anything. You could extend anything. You could reshape the behaviour of the system at any level, including the level at which behaviour is dispatched. CLOS treated the programmer not as a user of the object system but as its co-author. The system was a workshop, not a cathedral, and the door was always unlocked. This mattered to me practically, not just aesthetically. When I built HotLisp for the Royal College of Music in Stockholm (a Common Lisp implementation written in C that treated MIDI events as first-class citizens), I was already integrating music directly into the computational core. When I later built Igor Engraver, CLOS became the substrate on which the entire semantic model rested. The ability to extend, compose, and reshape; to add new types of musical objects that participated seamlessly in existing operations; to layer specialised behaviour onto generic frameworks without breaking them: these weren't theoretical luxuries. They were the reason the software worked. I taught algorithmic composition using Lisp at the Royal College. My students, who were composers, not computer scientists, understood the freedom immediately. You could express musical ideas directly, without the mediation of a language designed for someone else's problems. The gap between thinking and making was as small as I have ever seen it in any medium. Having Been ThereI am, as of this writing, sixty-four years old. I have been programming for over fifty years. I say this not as credential but as context, because having been there changes what you see. I was there for the AI winter. I watched a field that had promised the moon retreat into a decade of funding cuts and reputational collapse, dragging Lisp's standing down with it. The language didn't deserve the association; it was punished for the sins of researchers who had over-promised. I kept using it anyway, because the language hadn't changed, only its fashionability. This was the first time I learned that technical merit and cultural standing are largely unrelated. I was there for the object-oriented conquest of the 1990s. I watched as C++ and then Java established OOP as orthodoxy, complete with design pattern catechisms and UML diagrams that looked more like religious iconography than engineering documentation. CLOS, which had solved the problems these languages were struggling with years earlier, was invisible because it ran on the wrong platform and belonged to the wrong tribe. The industry chose what was marketable over what was good. This was the second time I learned that lesson. I was there for the web's arrival, which changed everything about deployment and nothing about the fundamental problems of building complex software. I was there for the enterprise Java years, when 'architecture' meant XML configuration files and dependency injection frameworks and a peculiar conviction that more abstraction was always better, regardless of what it cost in comprehensibility. I was there for the functional programming renaissance, which rediscovered principles that Lisp had embodied since the 1950s, often without acknowledgement and frequently with a missionary zeal that replicated the very dogmatism it claimed to reject. I am here for the current AI explosion, which rhymes with the original AI boom in ways that make my skin prickle. I have seen this arc before: extraordinary technical promise, sweeping claims about imminent transformation, and a market that moves faster than the underlying science. Time will separate what is durable from what is fashionable. None of this is complaint. It is observation. When you have been programming for fifty years, you develop a particular relationship with trends: you have seen enough of them arrive, dominate, and recede to know that the arrival tells you very little about the value and the dominance tells you nothing about the longevity. What remains after the trend passes is what was true about it. Immutability was true about functional programming. The JVM was true about Java. Interactive development was true about Lisp all along. The thing that has remained true for me, through every cycle, is the freedom principle. The conviction that the programmer should be trusted, that the tools should serve the maker rather than constrain them, that the ability to reshape your instruments is not a luxury but a precondition for doing serious work. I did not learn this from a manifesto. I learned it from a mainframe in 1973 and a Lisp prompt in 1979, and everything since has confirmed it. What Was Lost Igor Engraver was the fullest expression of this principle I had achieved. Eighteen people, $7.5 million in investment (2024 equivalent), a music notation system built entirely in Common Lisp that introduced semantic modelling to the field. Conductors were interested. Composers were interested. The architecture was sound. The project ended in 2002 after a combination of venture capital pressure, management-imposed feature creep, and the collapse of M&A activity following September 11, 2001. The technical architecture was sound; the business environment was not. I won't rehearse the full history here; it's documented elsewhere. What matters for this essay is what was lost and what survived. What was lost was the software, the team, and the years of accumulated work. What survived was the knowledge: of what works in music notation architecture, of what CLOS makes possible, of what fails when business logic overrides engineering discipline. And what survived, underneath all of it, was the conviction that the freedom principle was correct and that the work deserved to be done properly. Most people, having had a project of that scale taken from them, do not try again. I understand why. The rational calculus says the risk exceeds the reward. But the rational calculus doesn't account for the fact that you still know how to do it, that you've learned what you didn't know the first time, and that nobody else is going to build this. The ReturnWhen I came to Clojure, I came home. I don't mean this sentimentally. I mean it technically. Clojure is a Lisp; it thinks in the same shapes, provides the same minimal-but-sufficient primitive set, and trusts the programmer with the same radical freedom. But it adds things that Common Lisp never had and that my work desperately needed: immutable data structures by default, Software Transactional Memory for concurrent coordination, and the entire JVM ecosystem for deployment, GUI development, and cross-platform reliability. I've written extensively about these on the blog, so I won't rehearse the technical details here. What matters for this essay is the genealogical thread. I've called this 'Clojure for closure', and the wordplay is deliberate. It is a return to a native language after decades away, carrying everything those decades taught me. The CLOS patterns came with me. Ooloi uses Clojure's multimethods with the Methodical library to achieve CLOS-like method combinations: :before, :after, :around modifiers that layer behaviour exactly as they did in Common Lisp. The hierarchical type system uses Clojure's `derive` mechanism to create inheritance-like relationships without classes, mirroring CLOS's approach to multiple inheritance through composition rather than rigid hierarchy. Generic functions dispatch on argument types just as they did in CLOS, with operations defined outside the data they manipulate. Rich Hickey, Clojure's creator, made choices that resonate specifically with the tradition I come from. 'Mutability in bounded contexts is fine'. 'Solve problems first'. The emphasis on what you can build, not on what catechism you recite. Clojure didn't inherit the Lisp purity culture that never really existed in the first place; it inherited the Lisp builder culture, the one that judged you by what you shipped rather than which monads you could name. But Clojure also gave me something new. Immutability as a default changes how you think about time, state, and coordination. In Common Lisp, you could write immutable code, but the language didn't push you toward it. In Clojure, immutability is the grain of the wood. This inversion turned out to be profound for music notation specifically, because a musical score is a temporal structure where the meaning of any element depends on everything that precedes it. The semantic engine that results from this is something Igor Engraver, for all its strengths, could never have achieved. Where the Genealogy ArrivesOoloi's plugin system is, in a specific sense, the culmination of everything this essay describes.
The core engine is written in Clojure and architecturally closed. I've written about this elsewhere as the 'monastic core' principle. The freedom is at the perimeter: any JVM language can write plugins, and the plugin API speaks each language's idioms natively. A Java developer writes Java. A Kotlin developer writes Kotlin. Nobody is forced to learn Clojure or to understand the internals. The benefits of the architecture propagate without requiring conversion. This is the CLOS principle, expressed architecturally. The system trusts the developer. The developer works in their own language, with their own patterns, and the underlying architecture handles the coordination. The monastery uses Latin internally because Latin is precise; but when teaching farmers improved agriculture, you speak their language. This is not compromise. It is proper boundary design, and it is the lesson of fifty years in the workshop tradition. When I sit at this keyboard today, fixing a macOS menu bug or stripping debug symbols from a jlink runtime, I am using skills accumulated across the full span of what I've described. The mainframe taught me that serious work requires serious infrastructure. Lisp taught me that freedom is an engineering condition, not a slogan. CLOS taught me that the system should trust the programmer. The loss of Igor Engraver taught me that good architecture survives the business that funded it, if someone is stubborn enough to rebuild. Clojure taught me that immutability changes what's possible. The whole genealogy is present in every design decision, whether or not it's visible. I don't know if Ooloi will succeed, whatever that is. I know the architecture is right. I know the foundations carry what needs to be built on top of them. And I know where the freedom comes from, which is what I wanted to write down. 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 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:
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 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: 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.
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 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:
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 ProblemHere 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:
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 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 SatisfactionProgrammers 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 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 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 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. 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. 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 BaggageOne 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 DomainIt 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 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 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 ResultMost people already understand from school what a function is. 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 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) 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. VocabularyThese 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. 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. '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 DynamicsI'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 LatinumThe 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? 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 DeathElektra'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! 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. 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 ProblemWhen 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:
This isn't malice. It's accidental linguistic imperialism – systems that never considered the difference between internal precision and external accessibility. The Monastic PatternMedieval 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 WorksThe 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 CitizenshipWhen I say plugins are first-class citizens, I mean it precisely:
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. 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. 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 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:
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 HorizonsKnuth 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 FashionTeX 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. 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 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
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. 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? 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 StagesADR-0028 specifies the pipeline in five stages, separating connecting from non-connecting elements and applying a clear fan-out/fan-in pattern.
This separation allows Ooloi to exploit parallelism where possible and enforce order where necessary. Plugins as First-Class Citizens 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:
Simple articulations may use only the first two; beams may require all three. This uniform model ensures extensibility without compromising performance. Convergence by DiscomfortThe 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 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
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:
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. 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. 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:
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 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 OoloiGreetings, 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:
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:
Getting Started
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 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:
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 RecognitionAllow me to demonstrate the pattern I anticipated avoiding. Without a general traversal utility, each application would require its own approach: 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 RevelationWhat if I could write the temporal traversal once, then apply different transformations to it? Objective achieved: one traversal algorithm, many applications. But its architectural reach turned out to be even more profound. The Architectural InsightThe 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:
The transducer approach encouraged thinking in terms of composed processes:
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 ApplicationsWhat 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: 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: The transducer version processes one item at a time: 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: The pipeline stays the same. The data source changes. In Ooloi: Why This Matters Beyond MusicThe piece-walker solved a universal software problem: How does one avoid duplicating traversal logic whilst maintaining performance and composability? This pattern applies everywhere:
Transducers provide the infrastructure for "traverse once, transform many ways." The Bigger PictureBuilding 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:
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 ReadingRich Hickey's Essential Talks
Official Documentation
Educational Resources
Advanced Topics
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? |
AuthorPeter Bengtson – SearchArchives
April 2026
Categories
All
|
|
|
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.
|


















RSS Feed