Part 3 · 1 chapters · ~8 min

Functional Programming

Pure functions and referential transparency, immutability and persistent data structures, first-class and higher-order functions, closures, map, filter and reduce, composition and pipelines, currying, algebraic data types and pattern matching, errors as values, effects and monads without the jargon, and the functional core, imperative shell pattern.

4

Functional ideas in everyday code

code
// a pure fee function: no I/O, no clock, no globals → test with a table
const fee = (amountKobo: bigint, channel: 'app' | 'ussd'): bigint =>
  channel === 'ussd' ? 2_000n : amountKobo <= 500_000n ? 1_000n : amountKobo <= 5_000_000n ? 2_500n : 5_000n;

// functional core, imperative shell
async function handleTransfer(req) {                      // shell: I/O
  const [acct, limits] = await Promise.all([repo.account(req.from), repo.limits(req.from)]);
  const decision = decide(acct, limits, req, new Date());  // core: pure
  if (decision.kind === 'reject') return reply(422, decision.reason);
  await repo.apply(decision.postings); return reply(201, decision.transfer);
}

// errors as values in Rust; ? propagates them
fn parse_amount(s: &str) -> Result<i64, ParseError> { let n: i64 = s.parse()?; if n <= 0 { return Err(ParseError::NonPositive) } Ok(n) }

# Elixir: pipelines and pattern matching on tagged tuples
with {:ok, acct} <- Accounts.fetch(id), {:ok, hold} <- Ledger.hold(acct, amount) do {:ok, hold} end

Monads without jargon: Promise.then, Rust's ? operator and Elixir's with all mean the same thing: "run the next step only if the previous one succeeded, and pass its result along". That pattern, named, is a monad.

FUNCTIONAL PROGRAMMING, PRACTICALLY
ideas worth taking into any language
pure functionsSame input, same output, no sideeffects: trivially testable.immutabilityReturn new values; never mutateshared ones.higher-order functionsmap, filter, reduce, compose:functions as values.algebraic data typesSum types + pattern matching modelstates exactly.Result over exceptionsErrors as values: Rust Result, Go(val, err), Either.functional core, imperative shellPure logic inside; I/O at theedges.
swipe the figure sideways, or tap expand for full screen
1/5
purity
A pure function depends only on its arguments. Fee calculation as a pure function can be tested with a table of cases and no mocks.
no side effectstable-driven tests