Notes
2025/08/30

RC week 14: layered programming, symbiotic programming

What is better: A markup language like HTML, which is simple to understand and which does not try to be a programming language, or the “full package” of JavaScript and HTML, which gives you the power of a Turing-complete programming language in the browser?

The question sounds silly, and in some ways it is, because it is comparing apples and oranges: HTML is not trying to be a full programming language, it is a language for describing documents. This is by design, following the principle of least power:

The choice of language is a common design choice. The low power end of the scale is typically simpler to design, implement and use, but the high power end of the scale has all the attraction of being an open-ended hook into which anything can be placed: a door to uses bounded only by the imagination of the programmer.

Only make a language as powerful as it has to be, because that makes it easier to understand and use. Seen from this perspective, being less powerful than a Turing-complete language like JS is not a deficiency, it's a virtue.

Computer Science in the 1960s to 80s spent a lot of effort making languages which were as powerful as possible. Nowadays we have to appreciate the reasons for picking not the most powerful solution but the least powerful. The reason for this is that the less powerful the language, the more you can do with the data stored in that language. If you write it in a simple declarative from, anyone can write a program to analyze it in many ways.

And yet the story of the web is a story of making its languages increasingly more powerful, turning the web from a collection of documents into a platform of apps.

The temptation of power

Being able to use the full power of JS instead of being restricted to HTML is of course great if you're trying to build apps: The modern web makes it possible to build apps that are as powerful and polished as their native counterparts. But this comes at a price: While the “old web” allowed anyone to figure out how a website worked by just looking at the source, the compiled output of modern apps isn't human-readable anymore, even less so when apps are compiled to a lower-level language like WebAssembly.

Is this just the natural evolution of software? Do platforms start out by favoring simple and less powerful languages, but then gradually evolve towards more power, at the cost of being harder to understand? The evolution of the web seems to be the most well-known example of this trend, but far from the only one.

Is an increase in power at the cost of interoperability and understandability the inevitable outcome, leading ultimately to a world of apps as silos? Or is there an alternative, one that does not involve throwing everything away and going back to a simpler state of less powerful languages?

Layered programming

One option that seems to have been insufficiently explored is building systems out of layers of languages with different power, where less powerful (and less general) languages are built on top of more powerful (and more general) ones.

The internet protocol layers are an example of such a layered approach working (extremely) well in practice, but such a model hasn't really taken hold in other areas such as programming languages: We still treat languages as mostly isolated ecosystems that require a lot of initial investment, with interoperability between languages usually being something that involves a lot of friction.

There are a few languages specifically designed to be used within an existing ecosystem (such as Clojure on the JVM and Elixir on the BEAM) or embedded into applications (such as Lua), but even in these cases the tower of languages is mostly restricted to two layers, the host and the guest.

A notable exception seems to be the approach taken by OMeta and the STEPS project at the Viewpoints Research Institute, which was aimed at building a computing system using a large variety of domain-specific languages. But such an approach is rare. Most programmers just get to choose a single language and have to express the entire problem domain in it, which naturally means that they gravitate to powerful languages.

Symbiotic programming

How would languages look like if they were explicitly designed to be “stacked” and used within a hierarchy of languages, forming a tower of languages where the least powerful ones are built on top of more powerful ones?

In some sense, virtual machines can be viewed as a way of stacking an arbitrary number of languages, with each machine defining a clean interface that allows the guest to talk to the host. If the instruction set of a virtual machine allows it, it can then host another guest, without that new guest having to be aware of any of the lower layers of hosts that might be present.

What the evolution of the web showed us, however, is that less powerful languages will gradually accumulate power, unless we provide escape hatches that allow less powerful languages to access some of the power of the more powerful ones. This is mostly missing in HTML: It is hard to start writing a document and then incorporate some app-like features, it is often easier to just treat everything as an app, which has led to the proliferation of single page apps. (Although we are seeing some resistance to that trend in the form of htmx and similar tools.)

Effect handlers (and delimited continuations in general) might be an interesting way to integrate escape hatches into programming languages, because effects handlers and continuations make it possible to specify behavior while leaving some of the interactions with the outside world open.

A stack of virtual machines that can access lower layers through effects could lead to a style of programming that might be described as symbiotic: Such a style would not try to implement and solve everything in every single language ecosystem, but instead deliberately specify the less powerful but higher level parts in a higher level language that lacks most of the powerful low level constructs.

Would such a tower of symbiotic languages be able to resist the temptation of more and more powerful languages? Who knows, but it might be worth a shot.