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.
IO description, run only at the edge.
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.
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.
.copy. States the cost worry ("isn't copying wasteful?") honestly — and hands it to lesson 12.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.
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.
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.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.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.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.
@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.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.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.