In progress
OCaml & OxCaml
drafts in flight
A top-down, opinionated learning resource for OCaml and OxCaml - aimed at the working engineer who's heard the hype and wants to evaluate it. Every post is a buildable artefact: vanilla OCaml first, OxCaml side-by-side where it earns its keep, with the gossip and timeline that make it stick.
The list below is a public commitment device. Each entry is a draft in some state - pending, being written, or shipped. The series is sequenced; the project stubs at the bottom are larger builds.
Foundations
01 – 08Hardware up to OS - transistors, gates, CPU, memory bus, scheduling. Each post a buildable artefact. The hardware-end posts run side-by-side with Verilog and VHDL, with HardCaml (Jane Street's OCaml-as-HDL) closing the loop.
- 01
Modeling a CMOS transistor in OCaml
A transistor is a switch: voltage in, on or off out. Model it as an ADT, pattern-match the behaviour, watch exhaustiveness checking become hardware correctness. Side-by-side: the same primitive in Verilog and VHDL, and where HardCaml lets OCaml describe the gate directly.
Pending - 02
Building a bit vector library from scratch
Represent N-bit words as OCaml types. AND, OR, XOR, shift. Then OxCaml unboxed integers, side-by-side with the boxed version, and the perf delta on a real workload. Compared with Verilog's wire [7:0] and VHDL's std_logic_vector: what changes when bits are committed to silicon.
Pending - 03
★Composing gates into an ALU - and a circuit diagram generator
NAND from transistors → AND/OR/NOT → half-adder → full-adder → 8-bit ALU. Pure functions composing into bigger functions. The diagram generator emits Mermaid/DOT from the gate graph and gets reused throughout the rest of the series. Same ALU shown as a Verilog module and a VHDL entity; HardCaml as the bridge that keeps the description in OCaml all the way down.
Pending - 04
A tiny fetch-decode-execute loop in OCaml
Eight instructions, eight registers. Instructions as variants. Write the interpreter - the Nand2Tetris CPU in OCaml - and verify it with property-based tests in QCheck. Compared with picorv32, a real synthesizable RISC-V core in Verilog: different language, same shape, very different consequences for verification.
Pending - 05
Modeling memory hierarchy with access latency
Hierarchy as nested modules with different latency profiles. Cache locality affects simulated throughput. The fast path uses OxCaml's unboxed arrays. Diagrams via the post-3 generator.
Pending - 06
Disassembling OCaml: what does the native code actually look like?
Compile OCaml to native, disassemble with objdump. Walk the x86 - prologue, tag checks, GC write barrier. Then OxCaml's local_ and what disappears from the assembly.
Pending - 07
Build a stack machine interpreter in OCaml
Call stack as list, heap as hash map. Push, pop, call, return as pattern-matched ops. Then OCaml's runtime doing the same thing, and how tail recursion eliminates stack frames.
Pending - 08
Write a tiny OS scheduler in OCaml
Processes as records, run queue as priority queue, time slices as integers. Round-robin first, then CFS-style vruntime. Gantt chart via the diagram generator.
Pending
Finance modeled in OCaml
09 – 12Double-entry as a type constraint. Ledgers, statements, the accounting equation as typestate.
- 09
Model a double-entry ledger as an OCaml type system
The TigerBeetle insight: double-entry is a type constraint. Make debits = credits enforced by the type system - unbalanced transactions don't compile. OxCaml modes lock posted transactions to immutable.
Pending - 10
A household ledger REPL in OCaml
A tiny CLI ledger app: add income/expenses, query balance, export report. Builds on the type-safe ledger from post 9. Teaches dune layout, Cmdliner, S-expression serialization. First "real" tool in the series.
Pending - 11
The accounting equation as an OCaml typestate machine
Assets = Liabilities + Equity as a phantom type. Typestate enforces that unbalanced ledgers can't be queried. OxCaml uniqueness makes the guarantee zero-cost at runtime.
Pending - 12
Generate financial statements from a ledger
Balance sheet, P&L, cash flow as folds over an immutable transaction log. QCheck generates random valid transaction sets and verifies the accounting equation holds on every derived statement.
Pending
Memory systems
13 – 16Caches, virtual memory, mmap, allocators. How a working engineer's mental model of memory maps to OCaml code.
- 13
Simulate the memory hierarchy with realistic latency
Cache simulator: L1/L2/L3/RAM as chained modules returning (value, latency). Sequential vs random workloads. Plot the latency differences. SoA vs AoS via OxCaml in the simulator itself.
Pending - 14
Build a virtual memory system in OCaml
Page table as a hash map, page fault handling, mmap/munmap. TLB as a tiny cache of recent translations. Diagram generator draws virtual → physical mapping as an annotated grid.
Pending - 15
OCaml's own mmap: memory-mapped files in practice
Bigarray + Unix.map_file to actually mmap a file. 1 GB log reader. Measure mmap vs read(). Show the page-fault counter via /proc/self/stat. Connects to Irmin's content-addressed storage.
Pending - 16
Build a memory allocator in OCaml
Bump allocator → free-list → buddy allocator on top of Bigarray. Compare to OCaml's own minor heap bump allocator. OxCaml local_ as the logical conclusion: allocation cost equals zero.
Pending
Concurrency
17 – 21Races, memory ordering, lock-free structures, the case for OCaml 5 - and where the single-core bet wins anyway.
- 17
Data races in OCaml 5 - and proving them impossible with types
Three levels: a deliberate race with a mutable ref, the fix with Atomic, and OxCaml portability mode making the race a compile error. Detect, fix, prevent - escalated.
Pending - 18
Memory ordering in OCaml 5: a litmus test suite
Classic litmus tests as OCaml 5 domain pairs. Surprising results without barriers. Add Atomic with explicit orderings. QCheck generates random orderings and finds violations.
Pending - 19
Implementing a lock-free stack in OCaml using atomics
A Treiber stack with Atomic.compare_and_set. The ABA problem in practice. Fix with a tagged pointer. Benchmark vs a mutex-protected stack under contention. OxCaml unboxed: pack pointer plus version counter into one word.
Pending - 20
Mutex vs spinlock vs lock-free: a benchmark
All three implementations. Benchmarked across contention levels (2/8/32 domains) and hold times (10ns/100ns/1ms). Plot the results. Where each one wins, shown empirically.
Pending - 21
TigerBeetle's single-core bet: Amdahl's Law in OCaml
Model Amdahl's Law as a function. Plot the speedup curves. Build a toy payment processor: multithreaded with domains vs single-threaded. Benchmark under payment workloads. The design decision becomes measurable.
Pending
Runtime & observability
22 – 25Inside the OCaml runtime. Profilers, replay debugging, what domains are, what the scheduler is doing.
- 22
Build a memory profiler for OCaml programs using GC hooks
Gc.create_alarm, Gc.stat, Gc.compact. A tiny profiler tracks allocation rates, GC frequency, live heap size. Profile a deliberately leaky OCaml program and find the leak.
Pending - 23
Deterministic replay for OCaml programs: what rr gives you
An OCaml program with a deliberate intermittent data race. Normal debugging: bug disappears. With rr: record, replay deterministically, step backwards. Real rr replay + reverse-continue workflow.
Pending - 24
OCaml 5 domains are not threads: a mental model in code
The same counter increment program in three forms: Unix threads, OCaml 5 domains, eio fibers. Threads need a mutex (slow); domains need Atomic (fast); fibers need neither (cooperative). Execution timeline diagram for all three.
Pending - 25
Implement CFS (the Linux scheduler) in OCaml
CFS with OCaml's persistent balanced-tree Map as the run queue. Ten processes, 1000ms simulation, Gantt chart. Show how nice values distort fairness. OCaml's functional persistent map mirrors what CFS does mutably in the kernel.
Pending
Kernel interface
26 – 32Syscalls, signals, cgroups, namespaces, seccomp, eBPF - all from OCaml. The OS through OCaml's eyes.
- 26
★Hello World makes 50 syscalls. A MirageOS unikernel makes zero.
Strace native OCaml print_endline - about fifty syscalls at startup for the linker, locale, TLS. The same logic as a MirageOS unikernel makes zero - no kernel underneath. The most visceral demonstration of what a unikernel is.
Pending - 27
Graceful shutdown done right: the self-pipe trick, then the eio version
HTTP server two ways. V1: the self-pipe trick - signal handler writes one byte, event loop reads safely. V2: the same thing in eio where effects make signal handling look sequential. Why OCaml 5 effects matter for systems.
Pending - 28
Cage an OCaml program in a cgroup and watch the OOM killer strike
A memory-hungry OCaml program in a 100MB cgroup via cgcreate/cgexec. Watch the OOM kill. Instrument GC behaviour approaching the limit. TigerBeetle's static allocation: set the cgroup to exact footprint, never be surprised.
Pending - 29
Build a 200-line container runtime in OCaml
clone() via OCaml C FFI, spawn into fresh PID, mount and UTS namespaces, set hostname, chroot/pivot_root, run a shell inside. "Docker in 200 lines of OCaml." FFI to libc, namespace syscalls, what a container actually is.
Pending - 30
Wire two OCaml containers together with virtual networking
Extend the container runtime: veth pair, bridge, IPs. Two OCaml containers ping each other. The diagram generator shows the packet path. Overlay-network teaser for cross-host. Kubernetes networking demystified by building its bottom layer.
Pending - 31
Sandbox an OCaml program with seccomp
Use strace to discover the program's syscalls. Install a seccomp-BPF filter via FFI allowing only those. Demonstrate SIGSYS kill on a forbidden call. MirageOS minimal attack surface by construction vs seccomp as bolt-on.
Pending - 32
★An eBPF syscall explainer, written in OCaml
Attach eBPF from OCaml (libbpf FFI) to the sys_enter tracepoint. Capture and annotate every syscall a target process makes in plain English, real time. Run it against npm install or cargo build. Genuinely novel - no OCaml eBPF tooling, no educational annotator in any language. FP Launchpad collab thread.
Pending
Debugging deep dives
33 – 35LD_PRELOAD, gdb on OCaml native, core dumps across the FFI boundary. The unglamorous craft.
- 33
Spy on OCaml's garbage collector with LD_PRELOAD
OCaml's major heap is backed by C malloc. Write a malloc-interceptor .so, LD_PRELOAD it under an OCaml program, log every GC allocation to the C heap. Watch minor → major promotion live. Teaches dynamic linking, GOT, PLT. Bonus: why setuid binaries ignore LD_PRELOAD.
Pending - 34
Debugging OCaml native code in gdb: tagged values, mangled names, runtime frames
OCaml native is real x86; gdb works but looks alien - camlModule__fn names, ints shifted left with a tag bit, pointers one word past the header. A real session: breakpoint, backtrace through caml_ frames, decode a tagged int by hand.
Pending - 35
Read an OCaml core dump after an FFI crash
An OCaml program calls buggy C through the FFI and segfaults. Core dump, gdb, frame-0 null-deref pattern. Walk the stack from the C frame through the caml_ runtime back into OCaml code. Pinpoint the FFI boundary. OCaml is memory-safe until the FFI.
Pending
I/O and async
36 – 40C10K, epoll, three async runtimes raced side-by-side, perf flame graphs, raw block devices.
- 36
C10K in OCaml: thread-per-connection vs eio fibers, benchmarked
The same chat server, ten thousand idle plus a hundred active connections. Thread-per-connection (RAM explodes) vs a single eio event loop with fibers. Plot memory and throughput. C10K solved live, with numbers.
Pending - 37
Build an event loop in OCaml on raw epoll
epoll_create1/ctl/wait directly via Unix FFI. Hand-roll an echo server event loop. The reveal: this is eio under the hood. Building the primitive makes eio stop being magic. Diagram generator for the readiness flow.
Pending - 38
Three OCaml concurrency models race: Lwt vs Async vs eio
The same task (fetch ten URLs, combine) in Lwt (monadic), Async (monadic), eio (direct-style effects). The readability cliff. The OCaml concurrency evolution - 2008 Lwt, 2014 Async, 2022 effects - told as a code diff. The clearest argument for why OCaml 5 effects were worth an eight-year wait.
Pending - 39
Profile an OCaml hot loop with perf and a flame graph
Allocation-heavy OCaml - boxing floats in a tight loop. perf record → flame graph → GC dominates. Diagnose boxing, fix with OxCaml unboxed floats, re-profile. GC frames vanish. The full perf-engineering loop with an OxCaml payoff.
Pending - 40
Talk to a raw block device from OCaml
Open a loopback device with O_DIRECT via FFI, write sector-aligned blocks, read them back, bypass the page cache. Hit the alignment EINVAL footgun and fix it. TigerBeetle's storage philosophy in miniature. Connects to MirageOS's mirage-block typed module.
Pending
Major projects
P1 – P3Larger builds than a single post - ongoing projects with a technical thesis. Where they overlap with the numbered series, the post is the on-ramp and the project is the destination.
- P1
Syscall explainer in OxCaml + eBPF
Watch what Claude Code does to your kernel in real time, annotated in plain English. No OCaml eBPF tooling exists today. FP Launchpad collaboration thread (Navaneeth Nambiar).
Pending - P2
Nand to Tetris in OxCaml
Build a computer from logic gates up, entirely in OCaml. The "ground up" story told with full gossip and timeline - Tom Kilburn, Atlas, the whole arc.
Pending - P3
ripgrep-style Unix tool in OxCaml
A performance story showing OxCaml's unboxed types and SIMD-via-layouts versus vanilla OCaml. A community motivator: "here is what OxCaml unlocks."
Pending