Course overview

How to Design Programming Languages

47 modules
190 lessons
—
Part 1

Appendices

  1. Appendix A: Diagram Templates by StepSign in

  2. Appendix B: Mapping to Existing TechnologiesSign in

  3. Appendix C: Readiness AssessmentsSign in

  4. Appendix D: GlossarySign in

Part 2

Course Setup and the Incremental Ladder

  1. Course Setup and the Incremental LadderSign in

  2. Why "Structure to Interpretation"Sign in

  3. How to Use This CourseSign in

  4. The Incremental Ladder (Step 0 to Step 7)Sign in

  5. The Course LensesSign in

  6. Diagram Legend and Notation TypesSign in

Part 3

What Is a Programming Language?

  1. What Is a Programming Language?Sign in

  2. Languages as InterfacesSign in

  3. Models of ComputationSign in

  4. Design vs ImplementationSign in

Part 4

Syntax, Semantics, and Pragmatics

  1. Syntax, Semantics, and PragmaticsSign in

  2. Syntax vs Semantics vs PragmaticsSign in

  3. Static vs Dynamic SemanticsSign in

  4. Informal vs Formal SpecsSign in

Part 5

Homoiconicity and Code as Data

  1. Homoiconicity and Code as DataSign in

  2. Representations of CodeSign in

  3. HomoiconicitySign in

  4. Implications for Macros and ReflectionSign in

Part 6

Compiled vs Interpreted and Everything In Between

  1. Compiled vs Interpreted and Everything In BetweenSign in

  2. Execution Model SpectrumSign in

  3. Trade-offs and Where Complexity MigratesSign in

  4. Compilation as a PipelineSign in

Part 7

Diagramming Languages and Implementations

  1. Diagramming Languages and ImplementationsSign in

  2. Grammar DiagramsSign in

  3. AST and Evaluation FlowSign in

  4. Pipelines and Runtime DiagramsSign in

Part 8

Lexing: Turning Text into Tokens

  1. Lexing: Turning Text into TokensSign in

  2. Characters to TokensSign in

  3. Regular Expressions and Automata IntuitionSign in

  4. Hand-Written vs Generated LexersSign in

Part 9

Grammars and Parsing

  1. Grammars and ParsingSign in

  2. Context-Free GrammarsSign in

  3. Parsing StrategiesSign in

  4. Errors and RecoverySign in

Part 10

Designing a Tiny Expression Language

  1. Designing a Tiny Expression LanguageSign in

  2. Minimal Language SurfaceSign in

  3. Grammar and Precedence DesignSign in

  4. Parser Construction and ValidationSign in

Part 11

Abstract Syntax Trees and IRs

  1. Abstract Syntax Trees and IRsSign in

  2. Parse Tree vs ASTSign in

  3. AST Node Design and InvariantsSign in

  4. IR as "Compiler Currency"Sign in

Part 12

Tree-Walk Interpreters

  1. Tree-Walk InterpretersSign in

  2. Environments: Name-to-Value Mappings and the Boundary Between Syntax and ExecutionSign in

  3. Evaluation by Recursion: Interpreting Expressions Through AST Traversal and Rule ApplicationSign in

  4. Diagnostics and Stack Traces: Making Runtime Errors Explainable in a Tree-Walk WorldSign in

Part 13

Extending the Tiny Language with Statements

  1. Extending the Tiny Language with StatementsSign in

  2. Statements and Sequencing: Blocks, Sequences, and the Shift from "Expression Only" to "Program"Sign in

  3. Scope and Shadowing: Lifetime Rules as Semantics, Not SyntaxSign in

  4. Expression vs Statement Languages: Designing the Semantic Boundary and Its ConsequencesSign in

Part 14

Variables, State, and the Store

  1. Variables, State, and the StoreSign in

  2. Environment vs Store: References vs Values and Why Mutation Needs a Second StructureSign in

  3. Assignment Semantics: Modeling State Changes Precisely and Avoiding Hidden Aliasing SurprisesSign in

  4. State in Interpreters and IRs: Representing Mutation So Later Compilation Stays CorrectSign in

Part 15

Control Flow Constructs

  1. Control Flow ConstructsSign in

  2. Conditionals and Loops: Structured Control and How Evaluation Order Becomes ObservableSign in

  3. Desugaring: Translating "Nice Syntax" into Core Constructs You Can Reason AboutSign in

  4. Non-Local Control: Exceptions, Early Returns, and the Semantics of UnwindingSign in

Part 16

Functions, Closures, and Scope

  1. Functions, Closures, and ScopeSign in

  2. First-Class Functions: Lambdas as Values and What It Means for a Language to Treat Behavior as DataSign in

  3. Lexical vs Dynamic Scope: Closure Capture and How Name Resolution Becomes a Design ChoiceSign in

  4. Calling Conventions at a High Level: Call Stacks, Environments, and the Observable Consequences for DebuggingSign in

Part 17

Typing Discipline and Design Choices

  1. Typing Discipline and Design ChoicesSign in

  2. Static vs Dynamic Typing: Where Errors Surface and What You Can GuaranteeSign in

  3. Strong vs Weak and Mixed Regimes: What Coercion Buys and What It BreaksSign in

  4. Designing a Type Philosophy: Aligning Types with Language Goals, Ergonomics, and ToolingSign in

Part 18

Type Systems and Type Checking

  1. Type Systems and Type CheckingSign in

  2. Core Type Building Blocks: Primitives, Records, Pairs, and Algebraic Data TypesSign in

  3. Typing Judgments and Environments: Rules as the Executable Spec of Static SemanticsSign in

  4. Algorithmic Type Checking: Implementable Checking and What Makes Error Messages UsableSign in

Part 19

Type Inference and Polymorphism

  1. Type Inference and PolymorphismSign in

  2. Unification Intuition: Constraints, Solving, and Why Inference Reshapes API DesignSign in

  3. Parametric Polymorphism: Generics and Type Parameters as Abstraction MechanismsSign in

  4. Ad-hoc Polymorphism: Overloading and Type-Class-Like Ideas and Where Complexity AccumulatesSign in

Part 20

Operational and Denotational Semantics

  1. Operational and Denotational SemanticsSign in

  2. Big-Step vs Small-Step: What Each Style Explains About Evaluation and ControlSign in

  3. Denotational Semantics: Mapping Programs to Mathematical Objects to Reason About EquivalenceSign in

  4. Semantics for Transformation: Using Meaning to Justify Optimizations and RefactoringsSign in

Part 21

Static Analysis Beyond Types

  1. Static Analysis Beyond TypesSign in

  2. Dataflow Analysis: Liveness, Reachability, and the Shape of "Safe Approximations"Sign in

  3. CFGs and SSA: Why Control-Flow Graphs and SSA Form Dominate Compiler ReasoningSign in

  4. Soundness vs Completeness: How Analyses Become Warnings, Errors, and Developer GuidanceSign in

Part 22

From AST to IR: Lowering and Desugaring

  1. From AST to IR: Lowering and DesugaringSign in

  2. Surface vs Core Language: Designing a Minimal Core You Can Compile ReliablySign in

  3. Desugaring as Semantics Preservation: What Transformations Must Preserve to Stay CorrectSign in

  4. High-Level IR Design: Representing Control and Data for Compilation and OptimizationSign in

Part 23

Designing a Bytecode and VM

  1. Designing a Bytecode and VMSign in

  2. Bytecode Instruction Sets: Stack-Based vs Register-Based Design Trade-offsSign in

  3. VM Architecture: Dispatch, Frames, Stack/Heap Boundaries, and What Makes Debugging PossibleSign in

  4. Debug Metadata: Line Tables and Source Mapping as First-Class VM RequirementsSign in

Part 24

Code Generation to Bytecode

  1. Code Generation to BytecodeSign in

  2. Translating AST/IR to Bytecode: Preserving Evaluation Order and Side EffectsSign in

  3. Control Flow in Bytecode: Labels, Jumps, and Structuring Constructs Without Losing ClaritySign in

  4. Functions and Closures: Representing Calls, Environments, and Upvalues in a VM ModelSign in

Part 25

Interpreting and Optimizing Bytecode

  1. Interpreting and Optimizing BytecodeSign in

  2. Interpreter Techniques: Switch Dispatch vs Threaded Code and Why It Matters for PerformanceSign in

  3. Simple Compile-Time Optimizations: Peephole and Constant Folding as "Cheap Wins"Sign in

  4. Instrumentation and Profiling: Gathering Evidence to Guide Later Optimizations and JIT ChoicesSign in

Part 26

Exceptions, Error Handling, and Debugging Support

  1. Exceptions, Error Handling, and Debugging SupportSign in

  2. Exception Models: Unwinding vs Resumption and What They Imply for Language SemanticsSign in

  3. Debugging Interfaces: Breakpoints, Stepping, Inspection, and VM Support RequirementsSign in

  4. Tracing and Tool APIs: Designing Stable Hooks for Debuggers and Observability ToolsSign in

Part 27

Lowering to Low-Level IR

  1. Lowering to Low-Level IRSign in

  2. SSA Form: What SSA Guarantees and Why Optimizers Love ItSign in

  3. CFGs, Dominance, Phi Nodes: Encoding Merges and Control-Dependent Values PreciselySign in

  4. Mapping High-Level Constructs: Translating Closures, Objects, and Control into Low-Level IRSign in

Part 28

Code Generation to Native Architectures

  1. Code Generation to Native ArchitecturesSign in

  2. Calling Conventions and ABIs: Stable Contracts Between Compiled Code and the PlatformSign in

  3. Instruction Selection and Scheduling: Turning IR into Machine Code Without Leaving Performance on the TableSign in

  4. Multi-ISA Backends: Designing Portability Boundaries When Targeting Multiple ArchitecturesSign in

Part 29

Optimization Passes

  1. Optimization PassesSign in

  2. Local vs Global Optimizations: What "Global" Requires in Terms of Analysis and InvariantsSign in

  3. Canonical Passes: Inlining, Constant Propagation, DCE, Loop Transforms and Their Semantic ConstraintsSign in

  4. Trade-offs: Compile Time Versus Runtime Performance and How to Justify ComplexitySign in

Part 30

Garbage Collection and Memory Management

  1. Garbage Collection and Memory ManagementSign in

  2. Explicit vs GC: Ownership Ideas and the Boundary Between Language Semantics and Runtime PolicySign in

  3. Tracing, Reference Counting, Generational GC: Collectors as Different Failure and Latency ProfilesSign in

  4. Interactions: FFI, Concurrency, and Real-Time Constraints as the Hard Edges of Memory ManagementSign in

Part 31

JIT Compilation and Hybrid Execution

  1. JIT Compilation and Hybrid ExecutionSign in

  2. JIT vs AOT vs Interpreter: Selecting an Execution Strategy for Your Product ConstraintsSign in

  3. Hot Paths and PGO: Profiling-Guided Decisions and What "Hot" Really MeansSign in

  4. Tiering and Deoptimization: Why Adaptive Systems Need a Safe Escape HatchSign in

Part 32

Code as Data in Practice

  1. Code as Data in PracticeSign in

  2. Internal Representation vs External Syntax: Where Metaprogramming Hooks AttachSign in

  3. AST Manipulation and Reflection APIs: Capabilities, Invariants, and How Tools Stay CorrectSign in

  4. Risks and Guardrails: Hygiene Pressures, Invariant Breakage, and Why Tooling Becomes HarderSign in

Part 33

Macros: From Textual to Syntactic

  1. Macros: From Textual to SyntacticSign in

  2. Text Macros vs AST Macros: Failure Modes and Why Syntactic Macros Dominate Serious UseSign in

  3. Hygiene and Scope: Avoiding Accidental Capture and Keeping Expansions PredictableSign in

  4. Expansion Phases: Compile-Time Evaluation, Staging Boundaries, and ReproducibilitySign in

Part 34

Designing Homoiconic Languages

  1. Designing Homoiconic LanguagesSign in

  2. Uniform Representations: S-Expressions and What They Buy You in Simplicity and Transformation PowerSign in

  3. Quoting and Quasiquotation: Controlling Evaluation Boundaries and Building Code SafelySign in

  4. DSLs via Embedding: Using Homoiconicity to Create Language Families and Internal DSLsSign in

Part 35

Staging, Partial Evaluation, and Generative Programming

  1. Staging, Partial Evaluation, and Generative ProgrammingSign in

  2. Multi-Stage Programming: Staged IRs and the Semantics of Run Now vs Run LaterSign in

  3. Partial Evaluation: Specialization as an Optimization Technique with Semantic ObligationsSign in

  4. Code Generation Tooling: Templates, Builders, and Maintaining DebuggabilitySign in

Part 36

Metaprogramming Tooling and Debugging

  1. Metaprogramming Tooling and DebuggingSign in

  2. Inspecting Expansions: Viewing Expanded Macros and Staged Code as a Primary Debugging WorkflowSign in

  3. Source Maps and Expansion Traces: Preserving User Intent Through TransformationsSign in

  4. Safety Boundaries for Metaprogramming: Policy, Conventions, and Limitations That Keep Ecosystems SaneSign in

Part 37

Modules, Packages, and Namespaces

  1. Modules, Packages, and NamespacesSign in

  2. Module Systems: Visibility Rules, Encapsulation, and Compilation UnitsSign in

  3. Package Managers: Versioning, Dependency Resolution, Registries, and Supply-Chain ImplicationsSign in

  4. Namespaces and Imports: Avoiding Global Chaos While Keeping Code ReadableSign in

Part 38

Build Systems, Toolchains, and REPLs

  1. Build Systems, Toolchains, and REPLsSign in

  2. Toolchains as Products: Compiler, Linker, Runner, and What "One Command Works" RequiresSign in

  3. REPL Design: Interactive Evaluation, State, and Aligning REPL Semantics with Compiled SemanticsSign in

  4. Incremental Compilation and Hot Reloading: Feedback Loops, Caching, and Correctness PitfallsSign in

Part 39

Interoperability and FFI

  1. Interoperability and FFISign in

  2. Calling Out to Other Worlds: C/System Libraries and Runtime Integration StrategiesSign in

  3. Marshalling and Representation: Data Layout Across Boundaries and What Can Go WrongSign in

  4. Safety, Performance, Versioning: Maintaining a Stable Boundary Under EvolutionSign in

Part 40

Multi-Target and Multi-Backend Languages

  1. Multi-Target and Multi-Backend LanguagesSign in

  2. VM Targets vs Native: Portability Boundaries and Performance CeilingsSign in

  3. Shared Core IR: Designing a Common Middle Layer That Supports Multiple BackendsSign in

  4. Where Portability Lives: Choosing Which Features Are Portable and Which Are Platform-ShapedSign in

Part 41

Designing for Developer Experience

  1. Designing for Developer ExperienceSign in

  2. Diagnostics as Design: Error Messages, Hints, and How They Encode Language PhilosophySign in

  3. Formatting, Linting, Refactoring: Making Consistency and Correctness CheapSign in

  4. Language Servers and IDEs: APIs, Incremental Analysis, and the Ergonomics of Tooling IntegrationSign in

Part 42

Evolving a Language Over Time

  1. Evolving a Language Over TimeSign in

  2. Versioning and Deprecation: Migration Strategy as Part of the Language ContractSign in

  3. Backward Compatibility vs Progress: What You Preserve and What You Break, and WhySign in

  4. Governance Processes: RFCs, Community Norms, and Decision-Making Structures That ScaleSign in

Part 43

Language Design Patterns

  1. Language Design PatternsSign in

  2. Expression vs Statement Orientation: How Surface Form Shapes Semantics and ToolingSign in

  3. Effects and Explicitness: Implicit vs Explicit Control, Side Effects, and ComposabilitySign in

  4. Surface Syntax Patterns: Keywords vs Operators vs Punctuation and the Trade-offs in Readability and ParsingSign in

Part 44

Semantic and Type System Patterns

  1. Semantic and Type System PatternsSign in

  2. Safe-by-Default Design: Invariants You Enforce and What You Leave to ConventionSign in

  3. Effect Systems and Ownership Ideas: High-Level Patterns for Taming Side Effects and ResourcesSign in

  4. Refinement and Advanced Types: Power Versus Simplicity Versus Learnability as a Product ConstraintSign in

Part 45

Runtime and Execution Patterns

  1. Runtime and Execution PatternsSign in

  2. VM Patterns: Stack vs Register, Object Models, and Dispatch StrategiesSign in

  3. Memory Layout Patterns: Tagged Values, Headers, Vtables, and Representation LeakSign in

  4. Concurrency Models: Threads, Async/Await, Actors, Green Threads, and Runtime AssumptionsSign in

Part 46

Implementing DSLs and Embedded Languages

  1. Implementing DSLs and Embedded LanguagesSign in

  2. Internal vs External DSLs: Design Goals, Tooling Implications, and Maintenance CostsSign in

  3. Embedding vs Standalone: When a Host Language Is Enough and When It Becomes a ConstraintSign in

  4. New Language vs Library: Decision Criteria and Failure Modes of Each PathSign in

Part 47

Checklists for New Language Designs

  1. Checklists for New Language DesignsSign in

  2. Syntax Checklist: Grammar, Precedence, Readability, and Error Recovery as First-Order Design ConcernsSign in

  3. Semantics and Types Checklist: Evaluation Rules, Type Philosophy, and Static Reasoning GuaranteesSign in

  4. Runtime, Tooling, Ecosystem Checklist: Execution Strategy, Debugging Story, Packaging, and Long-Term EvolutionSign in