|
Creating a wind quintet with score and parts in Ooloi. Then adding doublings. No dialogs. Two years ago today, on 9 July 2024, the first commit to Ooloi went in. Time to take stock.
There's no music on screen. Two years in, and still not one printed note. No noteheads. No beams. No slurs. No page of music. All true, all planned, and all completely beside the point. Why? A notehead drawn in the first year would've been a comfort object, not evidence. So the visible surface stayed sparse on purpose while the real work went on underneath it. The absence of staves was never the interesting fact about this project. It was the cost of getting the interesting facts right, and this anniversary is the point at which they start becoming visible. This isn't an eccentric way to build notation software. It's how the serious ones are built, and the record is consistent. Sibelius took its two founders about seven years before the first release in 1993. MuseScore was carved out of a sequencer in 2002 and did not reach a stable 1.0 until 2011. Dorico, built by most of the old Sibelius team after they were let go, took close to four years and shipped its first version deliberately slow and incomplete, engine and engraving model first; the field judged the decision sound. Three programs, three decades, open source and commercial alike, and in every case the foundational years came before anything a user would recognise as finished. The counter-example proves the same point from the other side. Finale shipped fast in 1988, did more than anything before it, and became the industry standard for a generation. It was also, by wide agreement, punishing to use, and when its owners finally discontinued it in 2024 they said plainly that decades of accumulated code had made each new version harder to deliver and worth less. Feature piled on feature until the architecture could no longer move. That's the ceiling a notation program hits when the surface is built before the foundations are right. Two years isn't a long time to spend getting the foundations of a fifty-year program right, and I've never regarded it as a delay. Now, the bet on which Ooloi is built is functional programming. Music notation is a genuinely hard computer science problem wearing a friendly costume, and Clojure's properties looked to me like the right machinery for the parts of it that commercial programs have learned to manage rather than solve. Ooloi is my first Clojure project, though not my first Lisp by a long way, so the syntax was familiar ground; an old hand picks up a new dialect quickly. The deeper reorientation was immutability. Fully embracing immutable data structures, rather than merely tolerating them, changed how I thought about the whole domain, and almost everything architecturally distinctive about Ooloi fell out of that one shift. Undo, collaboration across machines, full use of multi-core parallelism, pervasive structural sharing: none of these is a feature bolted onto mutable objects. Each is a consequence of representing musical facts as immutable values. Learning to think that way was the real work of the first stretch. The harder part was psychological. A screenshot of notation would've been reassuring, and I could've produced something theatrical early on if theatre had been the goal. I grew up around theatre. I know a backdrop when I see one. I also know there's never a wall behind it. So the project spent a long time with most of its real work below the surface. The documentation was public, the ADRs were public, the blog was public, but the evidence still required reading. And the focus on the yet invisible might have given the impression that progress had been slow. So I drew up a list of what's been completed over the past two years. I expected it to be useful. I did not expect it to be quite so long. That surprised me, which is perhaps the point of doing the accounting at all. Daily work doesn't feel like accumulation. It feels like the next ticket, the next invariant, the next small refusal to leave a problem in a shape that'll charge interest later. Only when the pieces sit next to each other does the scale become visible:
During daily work, a subsystem closes and vanishes from attention. That's partly the method: finish something completely enough that it stops sharing the desk. The Instrument Library no longer asks to be designed. Localisation no longer asks to be invented, and the interface already speaks twenty-two locales. Undo no longer waits behind every future feature (with a small knife). Transport no longer converts itself through protobuf in-process for no reason. Window lifecycle no longer belongs to individual windows. Each closed subsystem removes one more thing from the cognitive field. The list makes visible what the surface hid. Ooloi hasn't spent two years waiting to become a notation program. It's spent two years removing the taxes a notation program would otherwise pay forever. This is also where the unusual working conditions matter. I've been ... unburdened by people. That sounds harsher than I mean it, though perhaps not by much. Not unburdened by readers, musicians, criticism or conversation, which have all mattered, but by the organisational forms that force software to explain itself before it has finished thinking. No investors, no release calendar, no committee, no inherited user base demanding that a workaround stay sacred because muscle memory has mistaken it for design. A team would have made some things faster and some things impossible, or at least premature. There's a stage in a project like this where the architecture has to be held whole in one mind, not because one mind is superior to several, but because dividing the thing too early changes the thing. Ooloi needed a period before that could happen. That period has had its own cost. Solitude removes obstruction, but it also removes alibis. Every accepted decision is mine. Every inconsistency in the documents is mine to catch. Every time Claude Code proposes a convenient shortcut, I have to know whether it's harmless, local, or the kind of thing that looks harmless now and grows teeth later. There's no one else to blame, which is excellent architecture and poor comfort. The AI has made the solitude more productive, though only because the architecture constrains it. Claude works best as an implementor inside settled boundaries. Give it one subsystem, clear invariants, failing tests, and a narrow target, and it moves with astonishing speed. Ask it to hold the whole system in its head at once, and it becomes what all of us become under those conditions: less intelligent than advertised. Every subsystem sealed below the musical floor is one fewer thing it has to juggle when the visible engraving work begins. That's another reason the long groundwork was worth doing. The cleared desk isn't a metaphor I use lightly. It's how the next phase becomes possible. The 3 June proof mattered because it compressed much of this into one modest action. Two laptops. One Ooloi hosting, one connected. A flute name changed on the guest, visible on the host. Undo on the host, reverting the guest's edit. The revert appearing on the guest. As cinema, it will not trouble Bergman. As architecture, it was the exact kind of evidence I had been waiting for. That little proof did not show engraving, and it did not pretend to. It showed authority, transport, shared state, invalidation, fetch, undo routing, and cross-machine propagation acting together without ceremony. Nearly two years of decisions appeared as a small correction to the name of a flute. Software milestones are sometimes like that: not fireworks, just a door opening without sticking because someone spent a very long time caring about the hinge. Now the work is moving towards the page. The custom picker is already visible and operable. It's not a native file chooser with new paint. It asks the backend what exists, receives opaque tokens, and never sees paths. Local storage and remote storage go through the same contract. The boring dialog is already the front edge of the piece model. Behind it, the piece window is now a live view of authoritative backend state, and this anniversary comes with a second small proof to show it. The video accompanying this post was recorded on two machines. On one, a wind quintet is assembled in a handful of gestures: flute, oboe, clarinet, horn, and bassoon dragged out of the Instrument Library into the Musicians panel of a shared piece window, each becoming a musician with its staves beneath it, and then dragged across into the Layouts panel to define the full score and the five individual parts. On the other machine, the same piece window answers. No delta is sent between them: a structural change names what is now stale, and the other screen refetches the canonical structure and redraws. It's the 3 June proof one storey higher. In June a flute changed its name and the plumbing held; here the same architecture carries piece structure rather than a library entry, edited collaboratively, with the invalidate–fetch–replace model the Instrument Library established generalising to the piece itself unchanged. And it's still, deliberately, not engraving. What crosses the wire and appears on the second screen is musical structure, not a rendered stave. There's no notehead in the video. The machinery that'll carry the music is running, and running across two machines, before the music arrives. Next come piece preferences and window persistence, the rest of the Piece Window arc. After that, MusicXML, because real scores must drive real notation work. Then Skija and the rendering pipeline. Then the noteheads: spacing, stems, flags, dots, accidentals, voices, collisions, beams, ties, slurs, hairpins, lyrics, pedalling, ottavas, tuplets, and the geometry of the page. The evidence will change again at that point. The collaboration you can already see in the video will still be there, but it'll be moving notation rather than structure. A slur will find space. A beam will sit, straddle, or hang. A note in a chord will change geometry around it. One screen will alter shared musical notation and another will answer with the same page. The program will make the argument in engraving, not only in structure. I'm looking forward to that more than I can sensibly say. Two years in, the thing that surprises me isn't that there's still no music on screen. I expected that. The noteheads were never beside the point as a goal, only as evidence; a program isn't proved by what it can draw first. The surprise is how much of the project is already behind the moment when music appears. The empty stave, seen from the surface, suggests absence. The inventory tells a different story: a great deal has already been decided, tested, closed, and made boring. Boring is underrated. Boring is what a foundation becomes when it's doing its job. So, coming out from the darkness that never really was, the next part will be visibly brighter, and with any luck it will involve fewer ... certificate files.
2 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. 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 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. Claude & Clojure It's no secret that I use Generative AI, specifically Claude Sonnet, to assist with the Ooloi project. I use it for writing Clojure tests TDD fashion, for generating Clojure code, for generating documentation, READMEs, architectural design documents and much more. Above all, I use Claude for exploring architectural strategies before coding even begins. It's somewhat reminiscent of pair programming in that sense: I'd never just task GenAI with generating anything I wouldn't scrutinise very carefully. This approach works very well and allows me to quickly pick up on good design patterns and best practices for Clojure. Claude & Python Overall, working with Claude on Clojure code works surprisingly well. However, this is not the case when I try to involve Claude for coding in Python, the main language I use as an AWS Solutions Architect. Generative AI struggles with creating meaningful Python tests and code – especially tests, which rarely work at all. This hampers its use as an architectural discussion partner and a TDD assistant. In fact, I've given up trying to use Generative AI for coding in Python. DifferencesI have a deep background in Common Lisp and CLOS, dating back to the 1970s. I've written Common Lisp compilers and interpreters, as many Lispers did in those days. The standard practice was to write a small kernel in assembler or C or some other low-level language, and then use it to write an optimising compiler on top of it to replace the kernel in an iterative fashion, sometimes using transformations of source code based on lambda calculus. (I still remember that paper by Guy Steele.) I see Common Lisp essentially as a big bag of good-to-haves (a really excellent one, mind you). As such, it was designed by committees over a period of decades. Clojure, on the other hand, is much tighter and rests solidly on consistently applied computer science design principles. Common Lisp is pragmatic and eclectic and thus somewhat sprawling in design. Clojure, in comparison, is smaller and much more focussed, even opinionated in nature, and for clear reasons. People attracted to Common Lisp and Clojure tend to be pretty well versed in computer science, especially Clojurians who generally have a good understanding of functional programming and immutable data structure concepts. Thus, the public code "out there" on sites like GitHub tends to be fairly advanced and of high quality. Python is an entirely different ballgame. It's one of the most commonly used languages today, and the public code base is absolutely enormous. This also means that the quality of that public code varies considerably. Also, Python is not functional at heart, and its data structures aren't immutable: side effects is the name of the game. Python programmers, though much greater in number than Clojure programmers, range from script kids to computer scientists. Thus, public Python code is much more all over the place and of varying quality. This may make it harder for a large language model to reason about. I wondered whether these differences accounted for the discrepancies in the usefulness of Claude and other LLMs as coding assistants in Clojure and Python. Asking ClaudeSo I decided to ask Claude 3.7 itself. I shared the Clojure codebase with it and asked why it thought results were so much better for Clojure than for Python. This is what it said:
It then continued, quoting the code base:
I guess this answers my question about Clojure vs Python. It's not just the functional and immutable aspects of Clojure, it's also specifying the domain stringently through design, architectural discussons, specs, and tests. The Ooloi project in particularWith all that out of the way, I then went on to describe how I use Claude as a discussion partner before code is generated, and the TDD approach I'm using where the tests of course describe the domain. Claude was almost embarrassingly enthusiastic:
I continued: "I also use Claude to create ADRs, READMEs, introductions for newcomers, Dev plans, etc. I'm quite open about this; transparency is paramount in an open-source project, and the idea is to facilitate collaborators' understanding of the Ooloi project as much as possible. This means that adherence to best practices, the use of sane architecture, abstracting away the complex stuff without compromising on performance or flexibility, etc, all are central." Claude responded:
We then went on discussing open-source strategies, tutorials and onboarding materials for contributors and much more which I'll spare you from for now. Finally, I asked it to summarise its views on Ooloi as a project:
Now, this level of enthusiasm and praise from an anthropomorphic-presenting LLM (essentially a glorified Markov chain) always makes me deeply suspicious – I'm absolutely allergic to toxic positivity, especially the kind from the USA – so I asked it "Are you sure you're not overly enthusiastic just to please me?". It replied:
I then asked Claude 3.7 to comment on my journey from Common Lisp to Clojure and on the influence from Common Lisp and CLOS on Ooloi:
And finally, I just couldn't resist asking: "What would Rich Hickey say?"
I guess that hypothetical assessment is good enough for me at this point. Still not quite sure that Claude isn't giving me a friendly reach-around, though... ;)
When I started programming in Lisp in 1979, after reading an article in BYTE Magazine, I hardly imagined that 45 years later I'd be embarking on a new Lisp adventure. Yet here we are, with FrankenScore (to be renamed Ooloi upon open-source release) – a modern music notation software built with Clojure. It's a project that brings together all my lifelong passions: music, programming, and the pursuit of elegant solutions to complex problems. The Path from Common Lisp to Clojure My journey with Lisp began in an era when optimising Common Lisp compilers were cutting-edge technology. I cut my teeth implementing Common Lisp interpreters and compilers (as one did in those days), delving into the intricacies of a truly original programming language. This experience shaped my understanding of what a powerful, flexible programming language could be. And now in 2024 I find myself in the world of Clojure, a modern Lisp dialect that runs on the Java Virtual Machine. The transition feels both familiar and novel. Clojure's emphasis on immutability and its handling of concurrency through Software Transactional Memory (STM) aligns with the functional programming principles I've long appreciated in Lisp. But it's not just about the language. The ecosystem around Clojure – the JVM, the interoperability with Java libraries, the rich set of tools and frameworks – provides a robust foundation that we could only dream of back in the Common Lisp days. CLOS Thinking in a Clojure World One of the more interesting aspects of this transition has been adapting CLOS-style thinking to Clojure's more data-centric approach. CLOS, with its powerful multiple inheritance and method combination features, encouraged a certain way of modelling problems. In FrankenScore, I've found myself reaching for these familiar patterns, but implementing them in Clojure's more functional style. For instance, the use of Clojure's protocols and multimethods, combined with hierarchies and the Methodical library, allows us to achieve CLOS-like polymorphism. It's a different approach, but one that feels natural once you embrace Clojure's philosophy. Clojure's deliberate avoidance of traditional object-oriented features felt immediately familiar and refreshing. It resonates with CLOS's approach, which many, including myself, have long regarded as transcending traditional OOP. Composition over inheritance, a principle I always valued even in the CLOS days, is not just a best practice in Clojure but the very fabric of its design philosophy. This alignment between CLOS's advanced features and Clojure's functional paradigm makes the transition feel natural and even inevitable. Changes in ThinkingPerhaps the most significant shift has been in embracing Clojure's emphasis on immutable data structures and pure functions. While these concepts weren't foreign in Common Lisp, they're central to Clojure's design. This shift encourages a style of programming that's inherently more thread-safe and easier to reason about – crucial for a complex application like FrankenScore. Another major change has been adapting to Clojure's more minimalist standard library compared to Common Lisp. This has led to a greater appreciation for carefully chosen, interoperable libraries and a more modular design approach. SimilaritiesDespite the differences, there are of course similarities in the overall approach. The emphasis on interactive development, the power of macros for domain-specific languages and the elimination of boilerplate code, plus the satisfaction of working in a dynamic, expressive language – these are all as present in my Clojure work as they were in my Common Lisp days. Moreover, the focus on solving complex problems through abstraction and composition remains. Whether it's CLOS or Clojure, the goal is still to create systems that are powerful, flexible, and pleasant to work with. Closing ThoughtsThis journey from Common Lisp to Clojure, from Igor Engraver to FrankenScore/Ooloi, is both challenging and rewarding. It's a testament to the enduring power of Lisp's ideas and the continued evolution of programming languages.
As I continue to develop FrankenScore, I'm captivated by the possibilities that Clojure and its ecosystem offer. While creating a powerful music notation software is the immediate goal, the project's scope extends far beyond that. It's an exploration of the synergies between music, technology, and open-source collaboration – a playground where these elements intersect and interact in novel ways. To those considering a similar journey, I'd say: embrace the change, but don't forget the lessons of the past. The parentheses may look familiar, but the world inside them is ever-evolving. |
AuthorPeter Bengtson – SearchArchives
July 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