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} endMonads 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
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