As part of ARIA’s Safeguarded AI programme, I have been leading the development of alifib, an experimental programming language and interactive proof assistant founded on higher-dimensional rewriting. In alifib, programs are diagrams: not illustrations of programs, but the syntax itself, the internal representation, the thing that is checked and run. This notebook post is an attempt to say what that means and what it is for. The proof assistant runs in a browser at http://compose.ee/alifib, with nothing to install, and the reader may want to keep it open beside the text.

The alifib web interface: three panes side by side. On the left, a code
editor with an .ali file open, syntax-highlighted. In the middle, a REPL pane
with a session-setup form (a type selector, source and target fields, a Start
button). On the right, a visualisation pane rendering a string diagram
(coloured nodes joined by wires) above a list of available
rewrites.

The web interface, with an editor, an interactive session, and a live string-diagram visualisation side by side.

From terms to diagrams

Nearly every programming language represents a program as a term, a tree-like structure. The tree records what the program is; how it runs is settled elsewhere—by reduction, by evaluation, by whatever mechanism an interpreter or compiler chooses—outside the syntax, and taken on faith.

Term syntax also makes a quiet assumption: that data may be copied and discarded at will. A variable may appear twice in a term, or never. Much of computation is not like that—a resource consumed cannot be reused, a message sent cannot be unsent—and category theory has long had a syntax for such situations: string diagrams for monoidal categories, that is, nodes joined by wires, each wire carrying a value, exactly once, from one operation into the next. Terms are the special case in which every wire comes with copying and discarding operations.

The second step is to go up in dimension. A string diagram is already a two-dimensional object, having besides its nodes and wires the regions of the plane that the wires bound. Once regions, wires, and nodes are counted as cells of dimension 0, 1, and 2, there is no reason to stop counting. A pasting diagram has cells of every dimension, each bounded by an input and an output face: a point, an arrow between points, a filled surface between two paths of arrows, a solid between two such surfaces. Each level, moreover, reads as a rewrite of the level below—an arrow carries its source to its target, a 2-cell carries one path of arrows into another—so that every storey of the tower, however high one climbs, is the same kind of object. (The counting is by role rather than by drawn shape: the string diagram is the Poincaré dual of a two-dimensional pasting diagram, with regions standing for points, and nodes for 2-cells.)

This is the entire ontology of the language: one kind of thing, the diagram, and one non-trivial operation upon it, subdiagram search, which finds a diagram sitting inside another. The whole rests on a single observation, the founding principle of higher-dimensional rewriting:

a computation on n-dimensional data is itself (n+1)-dimensional data.

In every other model of computation that I know of, a program and a run of that program are objects of different kinds: usually, a piece of syntax on one side, a sequence of machine configurations on the other. Here they are the same kind of object, one dimension apart, and the run is data again, ready in its turn to be computed on.

An alphabet of two letters

A type in alifib is a diagrammatic signature, a collection of building blocks of increasing dimension from which diagrams can be assembled. Let us look at a simple 2-dimensional example, essentially an encoding of a string rewrite system. Here is a type with a point pt, and two arrows a, b from the point to itself, so that words, that is, strings of as and bs, are one-dimensional diagrams. A rule of grammar for these words is nothing but a two-dimensional generator:

@Type
Letters <<= {
pt,
a: pt -> pt,
b: pt -> pt,
(* a rewrite rule is just a 2-dimensional generator *)
swap: a b -> b a
}

swap rewrites the pattern a b, wherever it occurs inside a word, into b a. Note what is missing: there is no separate register in which “evaluation rules” are declared, no computation mechanism external to the type. The rule and the data it acts on are generators of one type, a dimension apart. A point, two letters, and one rule are very little, but they are enough to compute with.

The editor pane with the Letters type as above; the Evaluate button has just
been pressed, and the output area below lists the elaborated type with its
generators pt, a, b, swap, each annotated with its dimension and
boundaries.

Evaluating the file. The interpreter checks that the boundary of every generator is a well-formed diagram, and reports what it has understood.

Watching it run

Begin an interactive session on the word a b a b. The visualisation pane draws it as a string diagram, while the engine searches out every position at which swap matches, and lists them. This is subdiagram search at work; everything else in the system is bookkeeping around it, and everything one is ever asked to trust reduces to it.

A rewrite session in progress. The visualisation pane shows a horizontal
string diagram: four wires in sequence labelled a, b, a, b. Below it, a
rewrite list with two entries, each naming the rule swap and the position at
which it matches. The second occurrence of "a b" in the diagram is highlighted
in colour, because the pointer is hovering over the corresponding entry in the
list.

Hovering over an entry highlights the subdiagram that the rewrite would consume, and clicking applies it.

Each click applies one rewrite. The session, however, is not mutating a string, as a text editor would: it is building a two-dimensional diagram, one swap cell pasted on at each step, and the word on screen is only the frontier of the construction. Stop at any point and switch the visualisation from the step view to the proof view: everything that has happened is there, as a single diagram.

The proof view after three steps: a two-dimensional string diagram read
bottom-to-top. Along the bottom boundary, the wires a, b, a, b; along the top,
b, b, a, a; in between, three beads, each an application of swap, joined by
wires that trace how each letter moved through the computation. The REPL pane
alongside records the session history: step 1, step 2, step 3, each with the
rule applied.

The trace is a 2-diagram whose input boundary is where the computation began and whose output boundary is where it ended, with everything in between retained. store gives it a name, after which it may be pasted into other diagrams like any other term.

It is worth pausing on that diagram. It is not a log of the computation; it is the computation, a first-class term of the language, which can be inspected, replayed, pasted into larger diagrams, and acted upon by higher-dimensional computations in its turn. If one only wants the answer, one asks for its output boundary.

Computational transparency

Recall the Curry–Howard correspondence, according to which programs are proofs: a well-typed term certifies, by existing, that the program it denotes is correctly formed. The correspondence, however, speaks only of the program. A term also carries an equational theory—a notion of when two terms are “the same”—and this theory, the very mechanism by which programs run, is meta-theoretical: assumed to exist, assumed to be computable, and in practice entrusted to the compiler. One may certify the program inside the language; to certify its execution, one must trust something outside it.

With diagrams, this boundary disappears. The rules by which a type computes are generators of the type itself, written down beside the ordinary ones, and a run of a program is a higher term—exactly the diagram built in the session above.

Well-typed terms are verified programs. Well-typed higher terms are verified executions.

I call this principle computational transparency, and it is alifib’s reason to exist. A verified program is not yet a verified run; between the two stands the whole tower of compilers and machines, and it is there that guarantees tend to evaporate. Our concern in this programme is with what a system did, rather than with what it was meant to do, and transparency is that concern made into syntax: the object one examines is the run itself, not a report of the run.

There is a consequence worth stating on its own. Since the rules by which a type computes are its own generators, each type is its own computational universe: its generators create, at the same time, the space in which computations happen, the data on which they act, and the computations themselves; nothing arrives from outside. A type expressive enough to house a Turing machine is still, in this sense, a sandbox, accounted for cell by cell by a kernel small enough to be read.

None of this is confined to two-letter alphabets. examples/TRS.ali sets up term rewriting systems in general, their terms two-dimensional diagrams and their runs three-dimensional ones, and examples/BinaryNat.ali instantiates it with binary arithmetic, so that

@BinaryNat
let d = (run auto on Eval.(run auto on parse "57 * (12 + 30)" end))

binds d not to the number 2394 but to a witness of the entire computation, whose output boundary is 2394. Since parsing, evaluation, and execution are all the same kind of diagram, parsing itself turns out to be functorial—the parse map sends parse traces of arithmetic expressions to the diagrams they denote—a remark that I will not develop here, beyond noting that those two files carry it out.

Eckmann–Hilton, by hand

Because equations are directed cells, equational reasoning in a higher-categorical structure is the same activity as running a machine. The classic Eckmann–Hilton argument says that two endomorphisms a, b of an identity must commute. In examples/EckmannHilton.ali the setting is built over a Bicategory module—itself written in ordinary alifib, its structural laws oriented into pairs of cells one dimension up—and the claim is stated as a map into the module’s type of equations, with two images withheld:

@EckmannHilton
let total Commutativity :: Equation = [
lhs => a b,
rhs => b a,
dir => ?,
inv => ?
]

Each ? is a hole: an image left open, to be filled interactively. The two fills are the proof: a pair of three-dimensional diagrams assembled by clicking through rewrites exactly as before, in which a and b slide past one another. Textbooks give this argument as a sequence of pictures, and the sequence built here is scarcely longer; the difference is that every move now exists as a cell, checked and retained, where the pictures rely on the reader’s goodwill to interpolate between them.

A hole-filling session on EckmannHilton. The visualisation shows a string
diagram with two beads labelled a and b sitting on wires of the identity
1-cell, mid-proof: b has slid past a and now stands to its left. The rewrite
list offers the structural moves available at this step. The REPL pane shows
the session was begun with "fill 0" and displays the goal: rewrite a b into
b a.

Filling the hole dir => ?. The proof that a b rewrites to b a is assembled move by move; done splices the finished diagram into the map.

Four readings

Beneath the syntax, an alifib type is a finite directed complex: a finite family of cells graded by dimension, each carrying a partition of its boundary into an input half and an output half. The combinatorics—the molecules of my book, Combinatorics of higher-categorical diagrams—descend from far-sighted work of Richard Steiner in the early 1990s, in a lineage that runs back through Ross Street to the early days of higher category theory. What holds the construction together is topological soundness: every such complex is realised by an honest cell complex with exactly one cell per generator, so the combinatorics never drift from the geometry they claim to describe.

The reward is that one object may be read, faithfully, in four ways at once: as a piece of directed topology; as a presentation of a higher category; as a higher-dimensional rewrite system; and as a directed higher inductive type, with constructors between constructors. Each reading is a way of using the language—one may reason equationally in monoidal categories and bicategories, specify abstract machines whose every run is a witnessed trace, or describe a space and ask for its homology at the prompt—and the readings illuminate one another. In examples/Bicategory_examples.ali, the walking equivalence (a morphism invertible up to invertible 2-cells) sits beside the walking adjoint equivalence, which adds the two triangle equations of an adjunction. Asked for their homology, the interpreter reports H2=ZH_2 = \mathbb{Z} for the first and H2=0H_2 = 0 for the second: a hole in dimension two, witnessing topologically the classical fact that a bare equivalence is not coherent—and that promoting it to an adjoint equivalence is precisely what fills the hole.

The REPL pane after two commands. "homology Equivalence" reports homology
groups by dimension with H_2 = Z; "homology AdjointEquivalence" reports the
same groups with H_2 = 0. Both types are loaded from
examples/Bicategory_examples.ali.

A type is a cell complex; its holes, counted by dimension, are one command away. Here the topology detects a categorical fact, the incoherence of a non-adjoint equivalence.

What comes next

alifib today is a working system: a batch interpreter, a terminal REPL, the browser interface pictured throughout this post, a line-protocol daemon for editors, and an MCP server offering the same engine, tool by tool, to AI agents. The last deserves a word. In alifib, building a proof is search, and search is untrusted, whether a human is clicking or a model is calling; what is trusted is the kernel that accepts or rejects each step. It does not matter, then, how capable or how erratic the model is: nothing it produces enters a type except through the same check that governs a human hand. This division of labour—an inventive but untrusted proposer set against a small checker—will sound familiar to readers from the Safeguarded AI programme.

alifib can be tried at http://compose.ee/alifib, and its source lives at https://github.com/ahadziha/alifib. A paper giving a full account of the language and its interpreter is currently under review.

Further off, modules will acquire higher cells of their own (a module is already, in disguise, a complex whose points are types); a strategy language, where the effectful part of the runtime will live, I/O included, is being worked out with Wessel de Weijer; and a small family of type constructors—duals, suspensions, Gray products, joins—will make globes, oriented simplices, and oriented cubes available structurally, rather than as constructions made by hand.