Blog
Notes from building Forma in public.
From Lists to Languages
A twelve-part series on building a typed Lisp from first principles with Effect TypeScript. It starts with a fifty-line interpreter and ends with a typed, extensible language for building other languages.
Lisp makes programs into data. Schema makes data meaningful. Effect makes meaning executable.
Posts
- Essay The Reader Is the Bottleneck
Machine-written code weakens the old case for macros and strengthens the case for abstractions that come with rules a compiler can check.
- Part 12 A Language for Building Languages
Declare forms in the language itself, build a vendor-review policy language from them, and run it with Effect services each host provides. The end of the series.
- Part 11 The Agent Can Read the Language
Give a coding agent schema-described tools instead of a text buffer: reports, types at positions, a rename the checker proves safe, structured replacement, and a sandboxed REPL.
- Part 10 One Program, Many Interpretations
Build a formatter, an analyzer, hover, and checked documentation over the language's typed representations, and combine them into one report with Effect.
- Part 09 Effects Are Part of the Type
Give function types a row of effects, add when, catch, and race, and let a host reject a program that needs a capability it doesn't have, before it runs.
- Part 08 The Prelude Is the Language
Shrink the compiler to a kernel of numbers, names, and calls, build lambda, a generalizing let, if, and match as typed forms, and run a pricing language on the same kernel.
- Part 07 Macros Are Programs About Programs
Add quasiquotation and pattern-based macros written in the language, make them hygienic by renaming, and see why homoiconicity alone isn't enough.
- Part 06 The Interpreter Becomes a Compiler
Elaborate surface syntax into a small typed core with resolved names, run it with an evaluator that trusts it, and save it as a Schema-checked artifact.
- Part 05 Types Before Execution
Represent types with Effect Schema, infer them with unification, and reject ill-typed programs before they run.
- Part 04 The Environment Is Not the World
Add lambda, closures, and lexical scope, and separate the names a program defines from the capabilities a host grants through Effect services.
- Part 03 Errors Are Not Exceptions
Rewrite the evaluator as an Effect with named, Schema-backed failures, selective recovery, and a clear line between program errors and interpreter bugs.
- Part 02 Giving the List a Schema
Give s-expressions a schema and source spans, and turn parsing into a Schema transformation with structured diagnostics.
- Part 01 Start with a List
Build the smallest Lisp imaginable, then see why code-as-data matters more than parentheses.