Runtimes
What happens underneath my program?
Follow the machinery.
Follow source into execution, meet the machine and operating system, inspect memory and evaluation, and understand the machinery that schedules your code.
Work in progress. 3 of 49 lessons are drafted and open to read; they're still being reviewed.
Useful foundations: values, function calls, scope, types, and errors. Revisit those lessons whenever a runtime detail needs context.
Still being written 3 of 49 lessons are drafted and open to read while they're reviewed. Planned topics show where the catalog is going.
The machine underneath
Start hereMeet the hardware your program eventually runs on, without needing to build it.
Booleans and logic gates
Build AND, OR, NOT, and XOR truth tables, then connect boolean expressions to the idea of a logic gate.
TypeScriptGoRustCHaskellJavaInstructions, registers, and the CPU
What does a processor actually do? Follow fetch, decode, and execute, and the small set of operations everything reduces to.
TypeScriptGoRustCHaskellJavaRegisters, caches, and main memory
Why is some memory faster than other memory? Compare the levels between a register and main memory, and what a cache miss costs.
TypeScriptGoRustCHaskellJavaReading a little assembly
What did the compiler turn this function into? Read a short listing and match it back to the source, without memorizing an instruction set.
CGoRustBuilding and loading a program
Build understandingFollow dependencies, build inputs, and initialization before examining the generated program.
Build commands and dependency resolution
What inputs does the toolchain need? Follow tsc, go, Cargo, a C compiler and linker, Cabal, and javac.
TypeScriptGoRustCHaskellJavaImports, dependencies, and initialization
What happens when a dependency is loaded? Compare dependency graphs, cycles, and initialization rules.
TypeScriptGoRustCHaskellJavaVersions, lockfiles, and reproducible builds
Why does the same project build differently on another machine? Follow version ranges, lockfiles, and pinned toolchains.
TypeScriptGoRustCHaskellJavaYour program and the operating system
Build understandingFollow what a running program asks the operating system for: memory, files, input, output, and an exit.
Processes and address spaces
What is a running program to the operating system? Follow a process, its own view of memory, and what separates it from others.
TypeScriptGoRustCHaskellJavaArguments, environment, and process startup
What exists before main runs? Follow how arguments and environment variables reach a program, and what each runtime sets up first.
TypeScriptGoRustCHaskellJavaSystem calls and the kernel boundary
How does a program ask the operating system to do something? Trace a library call down to a system call.
TypeScriptGoRustCHaskellJavaFile descriptors and handles
What does an open file look like from inside a program? Follow descriptors, standard streams, and closing what you open.
TypeScriptGoRustCHaskellJavaVirtual memory and the allocator
Where does heap memory come from? Follow pages from the operating system through the allocator to the value you created.
TypeScriptGoRustCHaskellJavaSignals, exit status, and termination
How does a program end, and who finds out? Follow exit codes, signals such as SIGINT and SIGTERM, and cleanup that may not run.
TypeScriptGoRustCHaskellJavaMemory, references, and layout
Go underneathConnect language behavior to storage while keeping conceptual diagrams distinct from measured layouts.
Addresses and storage
What is a memory address, and when does a language let you work with one? Compare physical storage with a diagram of bindings.
TypeScriptGoRustCHaskellJavaSize, alignment, and padding
Why can a record occupy more bytes than its fields add up to? Inspect a concrete layout and name its assumptions.
CGoRustJavaEndianness and byte order
How is a multi-byte number arranged? Compare memory inspection with a portable serialized representation.
TypeScriptGoRustCHaskellJavaEscape analysis and allocation
Does returning a value or creating an object imply a heap allocation? Explore what an implementation can prove and optimize.
GoJavaTypeScriptLocality, indirection, and caches
Why can two ways of storing the same data have different costs? Follow contiguous access and pointer chasing.
TypeScriptGoRustCHaskellJavaHaskell thunks, sharing, and retained values
What can a delayed computation keep alive? Inspect a named implementation instead of assuming a mutable variable slot.
HaskellEvaluation, retention, and reclamation
Go underneathDistinguish when work happens, why data stays reachable, and how storage becomes eligible for reclamation.
Leaks in garbage-collected programs
How can memory grow when a collector is running? Find caches, listeners, and closures that keep values reachable.
TypeScriptGoHaskellJavaFolds, strictness, and space leaks
When can a retained expression grow instead of being evaluated? Compare folds, strict accumulation, and retained data.
HaskellJVM heap generations and collectors
Why do most objects die young, and what does a collector do about it? Compare generations, pauses, and collector choices.
JavaTime, concurrency, and shared state
Go underneathReason about more than one activity after calls, mutation, lifetime, and failure are familiar.
Concurrency and parallelism
Can tasks overlap without running simultaneously? Separate task structure, scheduling, and multiple execution resources.
TypeScriptGoRustCHaskellJavaThreads, tasks, and goroutines
Who schedules each unit of work? Compare runtime tasks with operating-system threads.
TypeScriptGoHaskellRustJavaPromises, futures, and async/await
How do asynchronous results represent completion and suspension? Compare promises, futures, and languages with async/await; Java has futures without built-in await syntax.
TypeScriptRustJavaThe event loop and queued work
When does a callback or promise continuation run? Trace queued work and compare runtime-specific scheduling rules.
TypeScriptData races and synchronization
Which interleaving changes the result? Compare locks, atomics, and the visibility guarantees of a memory model.
TypeScriptGoRustCHaskellJavaChannels and message passing
Who owns data as it travels between tasks? Compare queues, channels, shared memory, and bounded capacity.
GoHaskellRustJavaCancellation, deadlocks, and task lifetime
What happens when work must stop or two tasks wait on each other? Follow cancellation, cleanup, and waiting dependencies.
TypeScriptGoRustCHaskellJavaWorkers and parallelism in JavaScript
How does single-threaded JavaScript use several cores? Follow workers, message copying, and shared memory.
TypeScriptGo select, sync, and context
How do goroutines wait, coordinate, and stop? Follow select, WaitGroup, Mutex, and cancellation through context.
GoRust Send, Sync, and thread boundaries
Which values can cross threads, and which references can be shared? Connect type guarantees to synchronization.
RustC threads, mutexes, and atomics
What does C give you for concurrency, and what is left to you? Follow pthreads or C11 threads, mutexes, and atomic operations.
CHaskell threads, MVar, and STM
How do threads coordinate through a shared runtime? Compare synchronized cells with composable memory transactions.
HaskellJava threads, executors, and virtual threads
How does Java run work concurrently? Compare platform threads, executor pools, and virtual threads.
JavaCompilers, virtual machines, and runtimes
Go underneathBring the pieces together: how language rules become executable work, and what the runtime supplies.
Parsing, syntax trees, and intermediate forms
How does source text become something a tool can analyze? Follow tokens, an AST, and an intermediate representation.
TypeScriptGoRustCHaskellJavaHow a type checker works
What does the compiler do with your types? Follow checking and inference over a small syntax tree.
TypeScriptGoRustCHaskellJavaCompilation and interpretation
What is translated, and what is executed? Compare strategies without treating compiled and interpreted as exclusive language categories.
TypeScriptGoRustCHaskellJavaLinking, loading, and modules
How are separately built pieces connected? Compare symbols, libraries, imports, and loading a program.
TypeScriptGoRustCHaskellJavaOptimization, AOT, and JIT
Why might the machine do less work than the source suggests? Explore ahead-of-time compilation, hot paths, and preserving observable behavior.
TypeScriptGoRustCHaskellJavaTypeScript checking and JavaScript execution
Which parts of TypeScript survive into JavaScript? Follow erased types, emitted code, and the separate JavaScript runtime.
TypeScriptGo runtime services and scheduling
What remains at runtime in a compiled program? Follow goroutine scheduling, stack growth, and garbage collection.
GoRust monomorphization and trait dispatch
How does a generic operation become executable code? Compare concrete instantiations with dynamic trait dispatch.
RustC preprocessing, translation units, and ABI
How does a declaration become a linked call? Separate preprocessing, compilation, symbol resolution, and platform calling conventions.
CGHC, thunks, and the runtime system
How does GHC turn Haskell into executable code? Explore delayed computations, garbage collection, and runtime services without confusing them with language guarantees.
HaskellBytecode and the JVM
What sits between Java source and machine execution? Explore class files, loading, verification, and runtime services.
Java