Chapter 1: Guided Tour
The warmup chapter from the book that just establishes some basics on operators, syntax, name binding, modules and such. More of an outside-overview, really.
Real World OCaml is supposed to be a intermediate-depth introduction to the OCaml ecosystem, with some rigour to drive home the intuition around the language constructs and under-the-hood mechanisms. It was fun to read this and to see patterns from other worlds like the Elixir world and OOP world be mapped to the world of OCaml and the greater ML-family of languages.

These notes aren’t polished yet, so some incoherence is expected.
The warmup chapter from the book that just establishes some basics on operators, syntax, name binding, modules and such. More of an outside-overview, really.
Chapter 2 is about the basics of name-binding into variables and functions and the encapsulation of logic. Names are bound, not assigned and shadowing of names is useful to manage bindings, fucntions are first-class values and function arguments vs let-bound names aren’t that different. Also shows pattern-matching is beautiful here and reminds me of Elixir-world too.
In this chapter, we see that OCaml lists are singly-linked, built right to left so it affects how we traverse and reason about them. Pattern-matching is the native grammar for working with them and most other structures. We worth together with the compiler to maintain correctness of our logic and the compiler helps us with exhaustiveness checks — pattern matching is mostly about how we do structural decomposition!
This chapter establishes the organisational backbone of any OCaml codebase: how files compile to modules, what module signatures are and why they’re the contract between implementation and interface, and how to write programs that are both modular and composable. Getting comfortable here is the prerequisite for everything interesting that comes later in the module system chapters.
Records are product types in OCaml that allow us to bundle named, heterogeneous data together. This forms the foundation for keeping data and doing functional updates, or keeping some fields mutable. The parts to be clear about are: field disambiguation, functional update syntax, mutable fields — they have some subtleties.
Variants is the start of my favourite parts about the language. Having sum types with exhaustive case analysis means that the compiler enforces that we’ve handled every branch — coupled togther with pattern-matching, encoding complex domain logic becomes a matter of making invalid states structurally unrepresentable. This chapter starts helping us see “types as theorems”.
Error Handling gives us a few approaches to handling unwanted situations. We need to know when to use which — so this chapters is more about interface design: how do we make error paths as ergonomic as happy paths? How do we force callers to deal with failure modes they’d rather ignore?
Imperative Programming is supported — turns out OCaml is a lot more general purpose than a learning like me initially would think. Knowing when to use it is important. The interesting tension is that OCaml’s imperative features look familiar but behave differently once the type system is involved: unit-returning functions, weak type variables, and the value restriction are all lurking here, and understanding them is essential for writing correct code that mixes pure and impure computation.
GADTs give a lot of power only when we understand its drawbacks and why. They shouldn’t be used when their need is felt. By allowing type parameters to vary by constructor, GADTs let us encode invariants in the type that would otherwise require runtime checks or defensive programming. The canonical example is a typed expression evaluator that statically guarantees no ill-typed evaluations — but the real-world use cases (typed heterogeneous containers, safe GADT-dispatched handlers) are where this becomes even more useful.
Functors are functions from modules to modules, and they are the primary mechanism for generic programming in OCaml. The chapter covers both the mechanics and the architecture patterns that functors enable: dependency injection, code sharing across different comparable types, and building libraries that are both type-safe and reusable.
OCaml operates at two distinct language levels: the core language of values and types, and the module language of structures and signatures. First-Class Modules are the bridge: we can now package a module as a value, pass it around at runtime, and unpack it back into module-land when we need it. This is how we get dynamic dispatch with static type safety — and it’s what makes plugin architectures and extensible systems possible without sacrificing the guarantees the module system provides.
Objects in OCaml are structurally subtyped instead of nominally. If a value has the right methods, it is the right type — no explicit declaration of inheritance or interface implementation required. This makes OCaml objects flexible in ways that feel almost like duck typing, except with full static guarantees. The chapter is worth understanding even if we rarely write objects, because the row-polymorphism that underlies them becomes relevant elsewhere.
Classes are OCaml’s mechanism for code reuse via inheritance — the recipe-for-objects that can be extended, overridden, and mixed in. OCaml’s class system feels more powerful than Java’s (virtual methods, multiple inheritance via mixins, binary methods) but it comes with complexity that the module + functor approach often avoids more cleanly. The underlying lesson is understanding when it truly is needed.
Association lists are the OCaml beginner’s first associative structure — and they’re \(O(n)\) for everything, which doesn’t scale. This chapter is the bridge to the two serious alternatives: purely functional balanced trees via Map (which uses functors to enforce comparability of keys) and mutable Hashtbl.
This set of notes goes beyond the scope of the concurrent programming chapter in the book (2nd edition) because in the OCaml ecosystem, there’s two phases to the journey of Concurrency / Parallelism: pre-OCaml 5 – cooperative concurrency as the typical concurrency model, with heavy reliance on libraries. Then there’s the Multicore Ocaml (~> 5) that changes things up (
notes found here). We’ll need to learn both to appreciate both phases.
Before OCaml 5, cooperative concurrency via Lwt was the dominant model for I/O-heavy OCaml systems. These notes trace the monadic plumbing of Lwt promises, the event-loop scheduler, and cancellation semantics — worked through tutorials such as the Mirage tutorial with annotated solutions and an analysis of how tail-call behaviour interacts with the monadic bind.
OCaml 5 removed the Global Runtime Lock and introduced Domains as the unit of true parallelism. These notes cover Domain-based programming, Domainslib’s work-stealing task pools, the relaxed memory model and DRF-SC guarantee, atomic variables for non-blocking communication, and blocking synchronisation with Mutex and Condition — a practical toolkit for parallel OCaml. We then go through Effects and Threads to complete the Concurrent programming picture.
The design of OCaml 5’s multicore garbage collector required retrofitting parallelism onto a 30-year-old single-core runtime without breaking backwards compatibility. These notes follow the original ICFP 2020 paper closely — covering the Streamflow-based major heap allocator, the two approaches to parallelising the minor collector, and the engineering decisions behind supporting weak references, ephemerons, lazy values, and fibers in the presence of concurrent GC threads.
OCaml’s type system is so strong that a large class of bugs simply can’t compile — which changes what testing is for. This chapter covers the OCaml testing ecosystem: inline expect tests via ppx_expect, property-based testing, and the general discipline of writing tests that complement rather than duplicate what the type system already proves. The snap-together quality of well-typed OCaml code means testing effort concentrates where it matters: the interesting invariants, not the trivial ones.
There’s a need to know the underlying OCaml memory model to be able to use other features like FFIs properly — because of the ways values are exchanged across C libs and the OCaml runtime. Also useful for interfacing with external system tools that operate on OCaml binaries (e.g. profiling tools, debuggers [who don’t have any knowledge of the static OCaml types]). So the learning of memory representation is done with translation between C world and OCaml world in mind.
OCaml, with its runtime that is a C library, handles most of the memory management for us. Routines exist that can be called by our OCaml program for heap block allocation to fill the heap up with OCaml values. What’s left is for us to understand how the Garbage collector manages the rest of the lifecycle. Note: pre-multicore OCaml garbage collection was NOT thread-safe, the modern one is, so these notes will contain information about the multicore-GC as well (in addition to what’s covered in the textbook).