Mere: Building a Language

A design diary of Mere, a small ML-family language with a region/view memory model, capability-passing effects, self-hosting in the browser, code generation to four backends (interpreter / C / LLVM / Wasm), and type-safe concurrency. Less about the finished product, more about the decisions: what was built, how, which problems came up, and how they were solved.

mereprogramming-languagecompilerlanguage-designtype-system

What this series is

Mere is a small ML-family programming language — Hindley–Milner type inference, algebraic data types, pattern matching — built up until it could type-check, evaluate, and compile itself in a browser, generate code for four backends (a tree-walking interpreter, C, LLVM IR, and WebAssembly), and run type-safe concurrency across all of them.

This series is not a tour of the finished language. It is a record of the decisions: what each piece is, how it was built, which problems showed up, and how they were resolved. Where a choice was a genuine fork, the alternatives and the reason for picking one are part of the story — including the times the answer was “don’t build this yet.”

The idea behind Mere

One belief shapes almost every decision:

Optimize for the verifiability of correctness, and make the implicit explicit.

Concretely:

  • The implicit is removed on purpose. Anything whose runtime representation or behavior silently changes is pushed into the open — memory ownership, effects, and (later) which values may cross a thread boundary all become things you can see in the types.
  • Compromise is unavoidable, so choose where. Every design page names the trade-off it is making rather than pretending there isn’t one.
  • Verbosity is acceptable. The code is written with AI assistance, so “more to type” is not the cost it once was. That frees the design to be explicit where a human-first language would hide things for brevity.

None of this is about being clever or fast. It is about being able to look at a program and know what it does.

How the pieces were decided

A pattern runs through the whole project and recurs in almost every episode:

  1. A paper trial — think a design through on paper (and often a throwaway prototype) before committing.
  2. Open questions, structured — undecided points are filed as Q-### and left open on purpose; they get answered as implementation forces the issue.
  3. Dogfooding — real programs are run through the toolchain to surface the problems a design document can’t.

The arc

  • Foundations — the philosophy, choosing OCaml as the host, and a minimal ML core that runs.
  • Memory — five strategies distilled into a unified region/view model, with Trivial, Drop, and with.
  • Effects — side effects passed as capabilities, with fine-grained borrow annotations, rather than hidden ambient state.
  • Code generation — one language, four backends, kept in lock-step.
  • Self-hosting — Mere’s own lexer, parser, formatter, evaluator, type checker, and code generator, written in Mere, running in the browser.
  • Concurrencyspawn / channels / par_map with Send / Sync type safety, across every backend.
  • What’s next — and, just as importantly, what was deliberately not built.

Start anywhere, but the parts build on each other roughly in this order.

← Back to Mere: Building a Language