Rusholme Logo

Rusholme

A toy Haskell compiler written in Zig

Built collaboratively with AI assistance. Exploring compiler construction, one curried function at a time.

Zig 0.16
Haskell 2010
LLVM
GRIN
JIT/lli
About

Why "Rusholme"?

Rusholme is an area in Manchester, England, famous as "the Curry Mile" — a vibrant street lined with Indian restaurants. Since currying is the signature technique from Haskell (after the mathematician Haskell Curry), the name was irresistible.

This project serves two purposes simultaneously:

Learning Zig

By building a real, non-trivial compiler from scratch

Understanding Compilers

Implementing a full Haskell frontend and multiple backends

Status: Actively in development

Fast

Zig + LLVM optimization for blazing-fast compilation

🌐

Multi-Backend

LLVM native, WebAssembly, JIT/lli, C, and JavaScript targets

🔥

Pure Laziness

Call-by-need semantics preserved through GRIN

🧠

AI-Assisted

Collaborative human-AI development approach

Architecture

Compilation Pipeline

A modern, pragmatic pipeline inspired by GHC but simplified for educational purposes

Haskell Source Lexer Parser AST Typecheck Desugar Core (F_C) Core → GRIN GRIN LLVM C / JS Wasm JIT/lli Haskell 2010 Recursive Descent System F_C Explicit Heap Native / Web / JIT

Frontend

Hand-written recursive descent parser with full Unicode support. Produces a complete Haskell 2010 AST.

Core IR

GHC's System F_C with type annotations. The type-safety firewall — if Core typechecks, later stages are trusted.

GRIN IR

Replaces STG+Cmm. Explicit memory operations (store/fetch/update) while preserving lazy evaluation semantics.

Runtime

Written in Zig. Handles heap allocation, thunk evaluation, I/O, and provides cross-platform runtime support.

Progress

Roadmap

View full roadmap
🚀

M0

Infrastructure

~70% complete
👋

M1

Hello World

~80% complete
📋

M2

Basic Programs

~79% complete
⚙️

M3

Optimisations

Not started

M4

Polish

~41% complete
Intermediate Representation

Why GRIN?

GRIN (Graph Reduction Intermediate Notation) is Rusholme's central IR, sitting between Core and the machine backends. Unlike GHC's STG, GRIN makes every heap operation explicit — there is no implicit allocation hiding behind calling conventions.

Stage Rusholme GHC Role
High-level IR Core (System FC) GHC Core (System FC) Type-checked, explicit types
Explicit-heap IR GRIN STG Allocation, thunks, evaluation
Low-level IR LLVM IR Cmm / LLVM IR Machine-level operations

A Concrete Comparison: swap

Consider this simple Haskell function that swaps the fields of a pair:

Nodes.hs
module Nodes where

data Pair = Pair Int Int

swap :: Pair -> Pair
swap (Pair a b) = Pair b a
ghc -ddump-stg-final Nodes.hs
GHC STG
swap = \r [ds]
  case ds of {
    Pair a b ->
      Pair [b a];
  };

\r is a calling convention marker ("re-entrant"), not explicit code. Pair [b a] allocates on the heap — but this is implicit, inferred by the STG-to-Cmm lowering from info tables and closure entry points.

rhc grin Nodes.hs
Rusholme GRIN
swap arg_0_1 =
  case arg_0_1 of
    (CPair a_2 b_3) ->
      store (CPair b_3 a_2) ;
      \node_5 ->
        pure node_5
    _ ->
      pure #"Non-exhaustive patterns"

store is the only way to allocate on the heap — it is a first-class monadic operation. The result is bound to node_5 via a monadic continuation \node_5 ->, making the heap address explicit and inspectable at every optimisation pass.

Explicit Allocation

Every heap write is a store instruction. Every heap read is a fetch. Every thunk update is an update. No hidden operations behind info tables or closure entry points.

Monadic Continuations

GRIN is a monadic IR: store returns a heap pointer that is bound to a fresh variable via \x ->. This gives every allocated node a named identity, perfect for whole-program heap analyses.

Analysis-Friendly

Because allocation is explicit and first-class, heap analyses (points-to, sharing, liveness) can operate directly on the IR without needing a separate abstract interpretation of calling conventions or info tables.

Further Reading

GRIN was introduced by Boquist & Johnsson (1996) in "The GRIN Project: A Highly Optimising Back End for Lazy Functional Languages". The key insight is that making heap operations first-class enables a family of whole-program transformations — including sharing analysis, dead heap elimination, and update avoidance — that are difficult to express on STG.

Documentation

Design Documentation

Full architectural documentation, design decisions, and rationale

DESIGN.md

Architecture & Decisions
Loading documentation...
Read full design document

ROADMAP.md

Milestones & Progress
Loading documentation...
Read full roadmap
Live Demo

Try Rusholme in Your Browser

Rusholme targets WebAssembly via its WASM backend, enabling browser-based Haskell evaluation without installing the compiler.

The REPL below compiles Haskell expressions to WebAssembly binaries (.wasm) right in your browser. Type expressions and press Enter to evaluate — multi-line blocks supported with :{ ... :}.

Rusholme REPL — WASM Backend

Press Enter to evaluate • Ctrl+C to clear • {:{ and :} for multi-line blocks

Join the Curry Mile

Rusholme is an open, collaborative project. Whether you're interested in compilers, Zig, Haskell, or just want to learn alongside us — there's a place for you.