Roadmap
Candor is a language for models, so the order comes from two kinds of evidence: how models do when they write it (evaluation), and what hurt while writing Candor's own compiler in Candor. Each milestone has a concrete way to tell it's done.
Ground rules for every change:
1. It lands in both compilers, the JS reference and the self-hosted candorc.
2. Both backends and both compilers stay byte-identical on every test, and the bootstrap still reaches its fixed point.
3. Each new rule comes with a diagnostic that says what to change, not just what's wrong.
4. It lands in llms.txt, the spec models read, and every example there must compile.
Done
v0.1: Core language done
Effects in signatures, typed holes, requires/ensures/example, labeled ends, no implicit conversions, trapping arithmetic, value semantics, exhaustive match. Two backends (instant JS and native C) held to byte-identical output by differential tests.
v0.2: Self-hosting done
inout parameters marked at the call site, borrowed arguments with copy-on-escape, string, file and argument built-ins, and candorc (the compiler in Candor) reaching a bootstrap fixed point. Then the first fixes from the field notes: ==/show on payload-free enums, top-level constants, and pop.
v0.3: Modules done
use "lexer.cnd", with each file checked as its own unit and diagnostics that name the real file and line. Names stay globally unique (no shadowing across modules), keeping "every name means one thing".
Done: candorc is six modules connected by use, build.sh is gone, and errors cite the real file (selfhost/lexer.cnd:46:19). Visibility is direct: deleting one use from cgen.cnd produces 165 not-imported errors, each naming the line to add. Traps name their file too.
v0.4: Distinct types and the first model evaluation done
type UserId = distinct Int: wrap with UserId(5), unwrap with .value, and no arithmetic. Every mix-up gets a hint that is the exact edit. candorc now uses NodeId and TyId. Retyping it surfaced 212 places where the two kinds of id met, including two bugs in my own refactoring scripts, and swapping one for the other is now a compile error. (Token indices stayed Int, since they never leave the parser.)
Also in this release: llms.txt, the spec written for models, and the first evaluation. Both models scored 9/9 from the spec alone, and the gaps they reported became a "precise semantics" section.
Next
Candor is for models, so the next milestone is about the feedback loop between models and the compiler, ahead of more language features.
v0.5: Model feedback loop next
- Machine-applicable fixes. Every diagnostic whose hint is an edit (add
effects io, add ause, wrap inUserId(...), writelet _ =) also carries a structuredfixin--json: file, range and replacement text, ready for a model to apply. - Evaluation round 2. Harder tasks: multiple files, 150+ lines, ids, holes, traps hidden in obvious solutions. More model families and sizes. Then the repair loop: give a failing model only the
--jsondiagnostics and count the rounds until it passes. - Spec-first changes. Each language change lands in
llms.txttogether with the code, andtests/spec.shkeeps every example in it compiling.
Progress: a local open model (Qwen2.5-Coder via Ollama) now runs through the repair loop. Two diagnostics that models couldn't act on were replaced: == became structural, and fences, slices and built-in redefinitions got hints that are edits. The loop now also runs the model's own example clauses. The 7B model went from 6/9 to 7/9 (details).
Done when: round 2 runs on at least three model families and the results page shows first-attempt rate, repair rounds, and the error codes models hit most. The worst-performing diagnostic gets rewritten, and the measurement is rerun.
v0.6: Generics and error propagation
Generic records, enums and functions, compiled by monomorphization in C. In the standard library: Option[T] and Result[T, E], with a visible way to propagate errors (for example try parse_expr(inout p)) inside functions that return Result.
Done when: the failed flag disappears from candorc's parser, read_file returns a Result instead of trapping, generic code produces readable errors, and models can use all of it from llms.txt alone (measured).
v0.7: Memory
Reference counting with copy-on-write in the native backend. Memory gets freed, and the remaining eager copies become lazy. This includes the arena copy that loading modules causes today.
Done when: the binary-trees benchmark peaks under 50 MB (453 MB today), self-compiling drops back to about 36 MB, and no benchmark gets slower by more than 10%.
v0.8: A typed AST for candorc
With generics and data-carrying enums, the compiler's arena of generic nodes becomes separate types for statements, expressions and types. The big dispatch functions become exhaustive matches.
Parallel tracks
Tooling
candor fmt, a single canonical layout;candor teston the native backend;--jsonforrunandtestoutput;- an index of error codes, with a page per code;
- editor syntax highlighting and a minimal language server (diagnostics and holes).
Performance
- A Ryu-style shortest float printer. Native
show(Float)is about 2 µs per call today, which makes the strings benchmark slower native than on JS. - Cheaper overflow checks on the JS backend's integer-heavy loops. Collatz is 13× slower there than native.
Standard library
Generic maps and sets (replacing candorc's hand-written StrMap), a string builder, sorting, and file and process I/O that return Result. The library stays small: one obvious way to do each thing.
Long term: Candor all the way down
Give candorc a JavaScript backend so the whole toolchain is written in Candor, and retire the JS front end. Because candorc could then compile itself to JavaScript, the self-hosted compiler could run in the browser, as an online playground on this site.
Not planned
- Operator overloading, implicit conversions, truthiness, macros. Each adds a second way for code to mean something, and that's where models get confused.
- Exceptions. Errors will be values (
Result) that the signature makes visible. - Terseness for its own sake. Verbosity is cheap for the writer, and explicitness is what the reviewer needs.