A 32-minute working session on the functional toolkit every developer can use today — pure functions, immutability, map/filter/reduce, composition, closures, and modeling absence with Option/Result — framed for JavaScript and TypeScript, with honest notes on when the simpler option wins.
Functional programming is less about a language and more about a habit: build your logic out of small pure functions— pieces that take values in and hand values back, and do nothing sneaky in between. Get that right and your code becomes predictable, testable, and safe to move around. It's the paradigm cousin of OOP & Architecture; most real codebases blend the two.
The pure function only maps inputs to an output. The impure one also pokes at a global and does I/O — the dashed arrows are the side effects.
Like a vending machine — press B4, get the same snack every time. A function that depends on the weather outside isn't a vending machine; it's a mood.
The single biggest source of "but it worked a second ago" bugs is shared mutable state: two parts of your program hold the same object, one quietly edits it, and the other breaks. The functional fix is immutability — treat values as read-only and build a new value instead of editing the old one.
Top: two names point at one array, so a mutation leaks. Bottom: the update returns a new array and the original is left intact.
Immutability removes the whole class of bug: if nothing can be edited in place, no one can edit it out from under you.
A higher-order function takes a function as an argument (or returns one). That one idea unlocks the workhorses of everyday FP: map, filter, and reduce — three building blocks that replace most hand-written loops with code that reads like the requirement.
Run a function over every element and collect the results. Same length out, each item reshaped.
Keep only the elements where the test returns true. Same items, fewer of them.
Walk the list carrying an accumulator, combining as you go. Many in, one value out.
map keeps the count and reshapes; filter trims; reduce collapses the whole list into a single value.
Because each returns a value, you can pipe them together. The chain reads top-to-bottom like a sentence — and there's no index, no temporary array, no off-by-one to get wrong.
Like a kitchen line — one station slices (map), one tosses out the bad pieces (filter), one plates it all into a single dish (reduce).
If pure functions are the bricks, composition is the mortar: wire small functions together so the output of one feeds the input of the next. Currying and partial application are the techniques that make functions easy to wire — by letting you supply arguments a few at a time.
compose(f, g)(x) means f(g(x)); pipe is the same idea read left-to-right, which usually matches how we narrate the steps.A pipe is an assembly line for data: the value flows through each small function in order.
Currying rewrites a multi-argument function as a chain of one-argument functions: add(a, b) becomes add(a)(b). Partial application is what that buys you — call it with some arguments now and get back a specialized function that remembers them, waiting for the rest.
add(1) is an increment; discount(0.5)is "half off".Supply some arguments now; get back a ready-made function that remembers them for later.
Two mechanics make the functional toolkit work. A closure lets a function carry private state without a class. Recursionlets a function express "do this, then do it to the rest" without a mutable loop counter. Both lean on the same idea: a function is a value you can capture, pass, and reuse.
next holds onto the n from its birth scope — nobody else can see or touch it.
Each call peels off the head and defers the rest, until the base case returns 0 — then the additions resolve on the way back up.
Two everyday things blow up programs: a value that might be missing, and an operation that might fail. The imperative answers — null and throw— are invisible: nothing in a function's signature warns you they can happen. The functional answer makes both explicit in the return type, so the compiler forces you to handle them.
Some(x) or None — replaces null. Result (a.k.a. Either) — either Ok(value) or Err(error) — replaces a thrown exception. Both turn an invisible possibility into a visible branch you can't forget to handle.The return type itself spells out both outcomes — the "nothing" and the "failed" paths are right there in the signature.
nulland exceptions don't show up in a type; an Option or Result does. The compiler becomes your checklist.Like a parcel that arrives marked either "contents inside" or "delivery failed: reason" — you open it knowing both outcomes exist, instead of being surprised by an empty box.
FP and OOP are not enemies; they answer different questions. OOP bundles state with the behavior that guards it; FP keeps data and behavior separate and leans on pure transformations. Most strong codebases in 2026 are hybrids — an object-oriented skeleton with a functional core.
OOP draws a boundary around data and its methods; FP keeps data plain and pushes it through a chain of pure functions.
You don't need a new language to write functional code. Pick on how much immutability help you actually need.
Pro — built in, map/filter/reduce everyone already reads, zero dependencies.
Con — shallow; no help enforcing deep immutability.
Choose as the default for everyday transforms — reach for a library only when this falls short.
Pro— write plain "mutating" code, get an immutable copy via proxies; tiny API.
Con — proxy magic adds overhead and a layer to debug.
Choose for deep, nested state updates (Redux / React reducers).
Pro — auto-curried, data-last utilities built for composition and pipes.
Con — point-free style can hurt readability; another dependency to learn.
Choose when you genuinely lean into currying/composition across a codebase.
Worth studying even if you never ship them — each pushes one functional idea to its limit.
Pro — purely functional, lazy, the strongest mainstream type system; teaches FP rigor.
Con — steep curve; laziness causes surprising space behavior.
Choose to learn FP deeply or for type-driven domains.
Pro — functional on the BEAM: lightweight processes, fault tolerance, easy concurrency.
Con — dynamically typed; smaller library ecosystem.
Choose for highly concurrent, resilient backend services.
Pro — a Lisp on the JVM with immutable persistent data structures by default.
Con — dynamic typing and Lisp syntax are an adjustment; JVM startup.
Choose for data-heavy, REPL-driven work on the JVM.
Pro — pragmatic FP on .NET: discrim- inated unions, pattern matching, clean OOP interop.
Con — smaller community than C#.
Choose for functional code that still needs the .NET ecosystem.
for loop is sometimes clearer than a clever three-step chain — readability wins.Five quick questions on purity, immutability, higher-order functions, closures, and Option/Result — instant feedback, no sign-in.
Navigate with ← → or scroll · back to library