00 β Overview
The arc in one page
MicroHs began on 2023-05-25 as two files: Lam.hs, 227 lines of Haskell
implementing a lambda-calculus-to-combinator compiler, and eval.c, 539
lines of C that evaluate the resulting combinator graph. That first commit,
ccb31388 ("First version"), is small enough to read in one sitting. There
is no parser worth the name, no error handling, no modules β just enough
machinery to compile a lambda expression to SKI-style combinators and run
it. This is the whole point of starting here: you can hold the entire
system in your head.
From there the author, Lennart Augustsson, grew the two files into a real
compiler. Four days in, commit a9178695 is bluntly titled "Make a real
compiler." β the toy evaluator gets an actual parser, a proper node
representation, and an optimiser. Growth from here is not linear or tidy.
The next day, commit 17587b2d is called "Hack around a
problem." β a fix that works, described as exactly what it is, not dressed
up as a design decision. That honesty runs through the whole history:
6f08f029, dated 2023-07-03, reads "Running IO still doesn't work. :(" and
is committed anyway, because committing broken states is how this workflow
works.
Over the next week or so the language fills out: let, infix operators,
tuples, lists, string and character literals, do-notation, modules, a
Prelude, list comprehensions β the surface syntax converges on something
recognizably Haskell, even though the back end is still combinator graph
reduction on a tiny C runtime. In parallel, the compiler learns to talk to
the outside world (monadic IO, serialization of program state to a file)
and grows a lab-notebook workflow of its own: command-line flags, ad hoc
tests, a first README.
The second half of the story is about the compiler outgrowing GHC. Starting
in July, commits accumulate around making the compiler's own source
compile under itself β commit messages like "Make it compile with uhs" and
"Make Desugar compile with uhs" appear repeatedly as each module is ported.
On 2023-07-20, 298c9824 renames uhs to mhs, and within the same
window the compiler successfully compiles itself. The last stretch, through
early August, adds a Hindley-Milner type checker and gets the self-hosted
compiler bootstrapping with type checking β a38c64ce, "Now bootstraps
with type checking.", 2023-08-10, is where this account stops.
That is 232 commits by one author over 77 days, and this repo's git history is the primary source β nothing here is a retelling from memory. The reader contract for the rest of this book: read chapters 01 through 09 in order (each assumes the state left by the one before it), and actually run the "Try it" blocks in a throwaway worktree rather than just reading past them. The value of this material is in watching working code evolve commit by commit, not in a summary of what changed.
Timeline
| Ch | Dates (2023) | Commit range | Chapter | Summary |
|---|---|---|---|---|
| 01 | 05-25 | ccb31388 + 21168890 | 01-the-seed.md | The seed: Lam.hs + eval.c |
| 02 | 05-26 β 05-30 | 21168890..b56ba28b | 02-a-real-compiler.md | A real compiler: parser, node repr, optimiser |
| 03 | 05-30 β 06-01 | b56ba28b..95ff6a34 | 03-growing-the-language.md | Growing the language: let, infix, tuples, lists, strings, do |
| 04 | 06-01 β 06-02 | 95ff6a34..8c5fbb1a | 04-talking-to-the-world.md | Talking to the world: monadic IO, serialization |
| 05 | 06-02 | 8c5fbb1a..8018eec8 | 05-lab-notebook-workflow.md | Lab-notebook workflow: flags, tests, first README |
| 06 | 06-02 β 06-07 | 8018eec8..41d6a7e8 | 06-becoming-haskell.md | Becoming Haskell: modules, Prelude, comprehensions |
| 07 | 07-01 β 07-18 | 41d6a7e8..a824a175 | 07-the-self-hosting-push.md | The self-hosting push: "compile with uhs" |
| 08 | 07-18 β 07-20 | a824a175..298c9824 | 08-bootstrap.md | Bootstrap: the compiler compiles itself |
| 09 | 07-20 β 08-10 | 298c9824..a38c64ce | 09-types-at-last.md | Types at last: HM checker, typed bootstrap |
Note the jump between chapters 06 and 07: the last commit of chapter 06,
41d6a7e8, is dated 2023-06-07; the first commit of chapter 07,
44004020, is dated 2023-07-01. Nothing in this repo's history explains
the 24-day gap β the log simply resumes. Chapter boundaries in this book
follow the thematic shape of the work, not a fixed cadence.
How to use this branch
Every chapter tells you which commit range to look at and what to build and run there. The convention, used throughout this book, is to check out a commit into its own worktree rather than disturbing your main clone:
git worktree add /tmp/mhs-<shortsha> <shortsha> && cd /tmp/mhs-<shortsha> && nix develop $MICROHS -c make <target>
$MICROHS is the absolute path to your clone of this repo, set once as
described in the top-level README.md. When you're done with a commit,
remove its worktree:
git worktree remove --force /tmp/mhs-<shortsha>
Reading the history directly, rather than trusting a paraphrase of it, is the whole method of this book. As a worked example, here is the actual first commit:
$ git show --stat ccb31388
.gitignore | 6 +
Lam.hs | 227 ++++++++++++++++++++++++++
Makefile | 14 ++
eval.c | 539 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 786 insertions(+)
Four files, 786 lines, one commit. That is the entire system chapter 01
starts from. When a chapter later claims a commit did something, you can
run git show <sha> yourself and check.
What this is not
This is not a Haskell tutorial β it assumes you can already read Haskell
comfortably. It is not a reference for modern MicroHs; the project kept
going long after this account ends, and none of that later history is in
scope here. This book is a close reading of one specific, bounded stretch
of git history: ccb31388 (2023-05-25) through a38c64ce ("Now bootstraps
with type checking.", 2023-08-10). It stops there, deliberately.