all_lessons / functional programming / index 15 lessons · ~4h read

Functional Programming, From First Principles

One linearized path from "why is this code so hard to change?" to a pure functional core wrapped in a thin effectful shell. Every lesson assumes only the ones before it; every abstraction — functor, monoid, monad — is built from a concrete problem and checked against its laws. The working language is Scala, but the ideas are the point.

The thesis, in one sentence: most difficulty in real software is the bookkeeping for mutable state and side effects, and functional programming is the discipline of minimizing and isolating both. The series runs in five movements — the why, the three pillars, the math vocabulary, the lawful patterns, and the data, evaluation, and effects that assemble the whole architecture. Read it straight through, or start at the orientation for the map.

Who this is for
You can read a little code in some language; no Scala and no math background is assumed. By the end you can say precisely what makes a function pure and why referential transparency lets you reason by substitution; model a domain so illegal states are unrepresentable; explain functor / monoid / monad as ordinary patterns with laws and recognize them in Option, List, and Either; replace null and exceptions with typed failure that composes; and assemble a program as a pure core that builds an IO description, run only at the edge.
Book source
An original first-principles path that follows the conceptual arc of Jack Widman's Learning Functional Programming: Managing Code Complexity by Thinking Functionally (O'Reilly) — complexity → immutability → purity → functions-as-values → the math underneath → functors/monoids/monads → typed failure → recursion → laziness → effects — without reproducing its prose, examples, or figures.

Part I · Why (lessons 00–02)

The problem FP solves, an operational definition you can check against any snippet, and just enough Scala to read every later example.

00
Orientation — FP as complexity control
The thesis: complexity is the context you must carry to understand one line, and its two engines are mutable state and side effects. FP's four commitments add up to one architecture — a large functional core wrapped in a thin imperative shell. Draws the whole 15-lesson dependency chain.
01
What is functional programming?
A checkable definition — pure functions over immutable data, built from expressions, composed by composition — contrasted with imperative/OOP on a single shared-mutation bug. Introduces the substitution model: replace an expression with its value and the meaning is unchanged.
02
Scala crash course for FP ideas
Just enough Scala to follow the series: val/var, everything-is-an-expression, case class and sealed trait, pattern matching, generics, List/Option, and lambdas. A targeted, FP-flavored tour, not a language manual.

Part II · The three pillars (lessons 03–05)

The load-bearing ideas: data that never changes, functions you can reason about by substitution, and functions you can compose.

03
Immutability — state, identity, and history
Why immutable data removes the aliasing/shared-mutation bug class, the value-vs-identity distinction, and copy-on-write updates with .copy. States the cost worry ("isn't copying wasteful?") honestly — and hands it to lesson 12.
04
Purity and referential transparency
A precise, checkable definition of a pure function and of referential transparency, the side effects that break them, and equational reasoning worked through on a pure expression — then shown failing on an impure one. RT is what makes substitution valid.
05
Functions as values
First-class and higher-order functions, lambdas, closures, currying, and partial application — culminating in composition (andThen/compose), the mechanism by which small pure pieces build large behavior.

Part III · The math vocabulary (lessons 06–07)

The precise language that makes the later patterns ordinary rather than mystical: functions as maps, types as sets, and the category that types-and-functions form.

06
Function math and types for programmers
A function as a map between sets (domain/codomain, total vs partial, injective/surjective/bijective) and types as sets. Product and sum types and their cardinalities make "make illegal states unrepresentable" a precise design tool.
07
Category theory as pattern language
A category is objects, morphisms, identity, and composition obeying two laws — and types-and-functions already are one. Read this way, category theory is a pattern language for lawful composition, and "lawful" means refactorings are safe. Sets up functor/monoid/monad.

Part IV · The lawful patterns (lessons 08–11)

The functor → monoid → monad escalation — map inside a context, combine many values into one, sequence context-carrying steps — then the typed-failure types you'll reach for daily.

08
Functors and natural transformations
A functor is a context F[_] with a map that changes values but never the structure, obeying the identity and composition laws. List, Option, and Either are functors; a natural transformation (e.g. Option ⇒ List) converts between them.
09
Monoids and fold
A monoid is a set with an associative combine and an identity. Associativity is the payoff: grouping doesn't matter, so a fold can be split and parallelized — the algebra behind MapReduce. foldLeft/foldRight and why reduce is the unsafe version.
10
Monads, flatMap, and for-comprehensions
The problem map can't solve — nested contexts F[F[A]] — and its fix, flatMap. A monad is a functor plus flatMap obeying three laws; for-comprehensions are sugar over flatMap/map; one interface sequences optionality, lists, async, and error.
11
Option, Try, and Either
Typed failure replacing null and exceptions: Option for absence, Either for a typed error channel, Try for capturing thrown exceptions. They're monads, so they compose in for-comprehensions and short-circuit on the first failure. A decision table for which to use when.

Part V · Data, evaluation, and effects (lessons 12–14)

The FP loop and why immutability is cheap, controlling when computation happens, and finally turning effects themselves into values — assembling the core/shell architecture from lesson 00.

12
Recursion and persistent structures
Recursion as the FP loop, tail recursion and @tailrec for stack safety, and persistent data structures whose structural sharing makes immutable updates cheap — updating one element of a million-entry Vector copies a handful of nodes. Closes lesson 03's cost worry.
13
Laziness, streams, and concurrency
Lazy evaluation (lazy val, by-name params) needs purity to be safe; LazyList separates the description of a possibly-infinite computation from its execution. Immutability plus purity means no shared mutable state — hence no data races. Future as a first taste.
14
Effects, type classes, and the road ahead
The capstone: the IO monad makes side effects lazy values you build purely and run only at the edge, realizing the functional-core/imperative-shell picture. Type classes are the encoding behind Functor/Monoid/Monad. Where to go next: Cats, Cats Effect, ZIO.
How to read this
Straight through, in order — the series is a dependency chain, and each lesson's "Linear position" box names exactly what it assumes and what new capability it adds. If you only have time for the spine of the argument, read 00 → 01 → 04 → 05 → 10 → 14.