Maturity Matrix

Monad is in alpha. This chapter says, area by area, what actually works today — so you can tell the parts you can build on from the parts that are still a sketch.

This rates the self-hosted compiler, the monad binary written in Monad that compiles itself. That is the language. Where the Rust bootstrap host differs it is called out, because the difference currently matters.

Every row here was checked against the compilers' own source and tests at the commit this book ships with, not inferred from intent.

How to Read This

LevelMeaning
SolidProduction quality for its scope. Well tested, unlikely to change under you.
WorkingDoes its job. Rough edges, but you can build on it.
PartialReal, but incomplete. Read the note before depending on it.
ExperimentalWorks, but the shape will change.
StubPresent in name only. Do not depend on it.
PlannedDoes not exist yet.
Host onlyWorks in the bootstrap host; the self-hosted compiler does not.

Language

AreaLevelWhat that means
Syntax & parserWorkingStable and well covered. Every def needs a type annotation — there is no top-level inference.
Type checkerWorkingBidirectional, with implicits and holes. Catches most errors; see the instance caveat below.
Type classes & instancesPartialResolution and superclass constraints work. But instances resolve at run time, so a missing instance type-checks and then fails with unresolved global:. Empty instance bodies do not parse, so class defaults cannot be inherited wholesale. Resolution keys on the type head, so an instance whose head is a variable (MonadLiftT m m, Monad (M I I)) type-checks and never dispatches.
Inductive typesWorkingParameters, recursion, indexed families, and the strict-positivity rule — a constructor field of a function type from the type being declared is rejected.
Pattern matchingPartialOne constructor level deep. No nested patterns, no literal patterns, no guards, no or-patterns, and no exhaustiveness checking.
StructsPartialRecords, field defaults, { s with … } update, keyword construction, and field destructuring all work. Generic structs are effectively unconstructible — use a type with a positional constructor.
Dependent typesPartialPi and forall types, implicits, Sort N universes, Prop, and length-indexed Vec all work. Propositional Eq type-checks but cannot be eliminated — Eq.rec's native is unimplemented and matching on refl fails at run time. No dependent pattern matching, no tactics, no universe polymorphism.
MacrosWorkingdefmacro, quote, and reflection-as-data. Term macros work here and not on the host — the one divergence in that direction.
Modules & visibilityWorkingpub/priv/package-private, explicit import filters, unused-import warnings. Resolution is mote-based — a mote's own root first, the directory cascade as the script-mode fallback — and check/test take --workspace or the mote you are standing in (compile takes an explicit path).
Tuples, raw stringsWorkingBoth real and stable.
OpticsWorkingLens and Prism in init.optics.
Indexed monadsExperimentalIndexedMonad, IndexedMonadState and IndexedMonadLift exist and type-check, and examples/indexed_monads.mo exercises them. The Monad (M I I) bridge instance in the prelude does not dispatch (variable head), so an indexed monad still needs its own Monad instance written out.
Termination checkingWorkingA structural subterm check, run once per module over that module's own declarations by both compilers, with the same message either way. Recursion over an inductive type is accepted; counting down an I64 needs #[terminating], #[decreasing ...] or #[partial].
Linear & affine typesPartialThe syntax parses everywhere it is meant to: struct fields, def and lambda parameters, and destructured parameters ((!{x, y} : P), which the host still rejects). Nothing is enforced — lowering drops the multiplicity, and every binder the checker builds is Many.
#[derive BEq BOrd Debug Lens]WorkingBoth compilers bridge the attribute to the same std/derive.mo macros. The backend must be imported — #[derive BEq] dispatches to derive_beq by name.
Char literals 'M'WorkingParse, type-check and compile, escapes included, \u{...} on both. Char itself is a stub: no operations, no BEq, no ToString — you can write, type and pass one, not inspect it.
_ holes in term positionPartialIn type position ((x : _)) it works. In value position both compilers accept it and both lower it to a value that is not what you meant (I64.beq _ 0 fails under each) — write the value. A hole in infer position, where no expected type reaches it (def k : I64 := (fn x => x) _), is rejected by both, with the host's own "cannot infer the type of a hole"; that divergence used to open the other way and closed on this branch.
Named instancesPartialinstance Name : Class Type { … } parses on both, bare or dotted (instance My.Greet : Greet I64). Nothing selects an instance by name in either implementation.
Brace-form params with defaultsWorkingThe declaration parses and the default is applied at call sites by both compilers: omit factor in scale { p := 4 } and the declared := 2 stands in.
Multi-binding let x := 1; y := 2 inWorkingThe ; separator parses on both. Write it: self-hosted, an omitted ; lets the binding's value swallow the next statement as an argument (see the appendix), so the binder never scopes. Nested let … in still works everywhere.
UFCS method calls (x.f args)PlannedAn earlier host type checker desugared these; neither does now. x.f is field access only.
Backtick infix (`f`)PlannedThe parser recognises the token; the expression parser never reduces it.
for loopsPlannedfor is reserved with no grammar rule.
Reading stdinPlannedIO can print and touch files; there is no getLine.

Implementation

AreaLevelWhat that means
Self-hosted compilerWorking~64k lines of Monad in lang/. It compiles itself, and the result type-checks the compiler's own source. CI runs this on every push. The bootstrap has reached a fixpoint.
LLVM native backendPartialArithmetic, strings, file I/O and the seven concurrency natives are wired. A program reaching an unwired native fails to compile rather than miscompiling — one of four fail-fast gates.
monad checkWorkingThe corpus checks clean, and the check phase of CI's sweep carries no exclusion list. check takes explicit paths, --workspace, or the mote you are standing in.
monad runWorkingCompiles the file and executes the binary in one step. The binary is always named run_out, so there is no -o.
monad evalPartialA self-hosted interpreter, but only 8 pure natives are wired into it (i64_add/sub/mul/eq/lt, string_concat/eq/to_lowercase). Anything else, println included, fails with unknown native — so it cannot run a hello-world.
monad versionWorkingPrints the git commit baked in at link time.
monad testWorkingRuns the whole corpus, including the concurrency tests, with no exclusion list: a compiled driver per file, per-test timing, module::test_name names, and a failure COUNT the driver reports through a result file (no longer an 8-bit exit code, so a file's test count is no longer capped at 255). Takes explicit paths, --workspace, or the mote you are standing in.
monad prettyWorkingParses and pretty-prints.
Memory managementPartialCompiled binaries use the Boehm conservative GC, explicitly a stopgap. The compiler still emits no monad_retain/monad_release calls for ordinary values, so the refcount is only load-bearing for fiber and scope handles (see below); deterministic freeing is blocked on linear types.
ConcurrencyExperimentalReal concurrency on 1:1 pthreads: forkIO starts a thread, await_fiber joins it, scope_* keeps handles alive through the atomic refcount. Cancellation is recorded rather than preemptive. The host is still the lazy/cooperative one, so only compiled code can actually interleave.
Error messagesPartialSource spans and useful text for most failures. Parse errors report "did not fully parse (stopped before end of file)" with the remaining text, which locates the line but not the construct.

Ecosystem

AreaLevelWhat that means
Standard libraryPartial~440 public definitions, 35 classes, 132 instances. Strong: the numeric tower (10 widths, fully instanced), strings, lists, HashMap/BTreeMap, a native-backed Array, a pure-Monad SHA-256. Thin: no Iterator, zero Traversable instances, duplicate Semigroup/Monoid.
Assertions (std.test)StubTest.assert is the identity function on Bool. No assert_eq, no failure messages.
DistributionPartialA nightly prerelease is published on every push to main, and scripts/monadup installs and switches between them. Two artifacts, both Linux x86_64: the compiler and a tarball of the init/std/runtime sources, which monadup unpacks into the same directory so an installed toolchain can compile a mote that lives outside a checkout. It is built inside the Nix devenv, so it links store paths and does not run on a plain machine.
PackagesWorkingManifests are read: a module path resolves from the mote doing the use, and check/test/compile take --workspace or the mote you are standing in. Script modules declare theirs inline with #![mote { … }]. No search-path flag, and no lockfile or registry — those stay host-only.
Editor toolingHost onlyThe LSP server, MCP server, REPL, and organize-imports all live in the host. No syntax highlighting for any editor, and no tree-sitter grammar.
CIWorkingLint, full test sweep, and a self-hosting bootstrap check on every push. The release build runs inside the test and bootstrap jobs, not as a separate gate. The sweep runs the self-hosted runner against a freshly self-compiled binary, so monad test itself is covered.
DocumentationPartialThis book. Every code block is type-checked; prose is not.
FormatterPlannedorganize-imports, in the host, is the only codemod.
Package registryPlanned
Doc generatorPlannedDocstrings are parsed and retained, but nothing renders them.
Debugger integrationPlannedDWARF is emitted by default now (--release opts out) and carries a distinct location per term, not one per top-level definition — but nothing consumes it yet.

Scale

For context on what "alpha" means here:

Monad source (.mo)~81,500 lines, of which lang/ is ~64,500
Rust source (bootstrap host)~56,400 lines
Monad tests (#[test])2,112 run by CI's sweep — every one through the self-hosted runner
Native functions137 declared in init/+std/; 3 unimplemented everywhere; the backend wires a subset
Standard library~440 public defs, 35 classes, 132 instances (excluding test modules)

The Short Version

Monad is a real, self-hosting language: the compiler is written in Monad, compiles itself, and reaches a fixpoint. The type checker, the class system, the module system, and a genuine macro system all work.

What it is not yet: safe by construction (the self-hosted compiler checks termination but not linearity — the multiplicity syntax parses everywhere now, and means nothing), scalable in concurrency (fibers are 1:1 pthreads with 128 MB stacks, real but not the design that ships), or distributable (nightly binaries exist; a lockfile and a registry do not). Its test runner compiles and runs a driver binary per test file, and covers the whole corpus with no exclusion list — every file that used to be reported as a gap now runs self-hosted.

The single largest gap is linear types — and because deterministic memory management is meant to be built on them, that gap is also why compiled binaries need a garbage collector.