Functional Programming
Building software from pure functions over immutable data, composed into larger behaviour. Interviewers probe it in every language now, because the reasoning payoff is the whole point.
on this pageshowhide
explore
- Purity & Referential Transparency22 questions
- Observable Side Effects4 questions
- Substitution & Equational Reasoning4 questions
- Memoization & Caching5 questions
- Safe Reordering & Parallelism5 questions
- Effect Isolation4 questions
- Immutability21 questions
- Persistent Data Structures4 questions
- Structural Sharing5 questions
- Shallow vs Deep Immutability4 questions
- Update By Copy4 questions
- Safe Sharing Without Locks4 questions
- First-Class Functions17 questions
- Callable Values4 questions
- Lambda Calculus Roots4 questions
- Function Types & Arity5 questions
- Callbacks & Deferred Work4 questions
- Higher-Order Functions21 questions
- Mapping & Filtering4 questions
- Folding & Reduction5 questions
- Flattening & Bind4 questions
- Currying & Partial Application4 questions
- Wrapping & Decoration4 questions
- Function Composition17 questions
- Compose vs Pipe Direction4 questions
- Associativity & Identity4 questions
- Point-Free Style4 questions
- Predicate Combinators5 questions
- Declarative vs Imperative16 questions
- Expressions vs Statements4 questions
- Loops vs Transformations4 questions
- Intent Over Mechanism4 questions
- When Imperative Wins4 questions
- Laziness & Deferred Evaluation20 questions
- Strict vs Non-Strict4 questions
- Thunks & Forcing4 questions
- Infinite Sequences4 questions
- Short-Circuiting4 questions
- Operator Fusion4 questions
- Closures & Lexical Capture22 questions
- Lexical vs Dynamic Scope5 questions
- Capture Semantics5 questions
- The Loop Variable Pitfall4 questions
- Private State & Factories4 questions
- Captured Lifetimes & Leaks4 questions
- Recursion and Tail Calls20 questions
- Structural Recursion4 questions
- Accumulator Passing Style4 questions
- Mutual Recursion4 questions
- Trampolines & Stack Safety4 questions
- Corecursion & Unfolding4 questions
- Pattern Matching16 questions
- Sum & Product Types4 questions
- Exhaustiveness Checking4 questions
- Destructuring & Guards4 questions
- Making Illegal States Unrepresentable4 questions
- Effects and Monads22 questions
- Functor, Applicative, Monad4 questions
- Modelling Absence5 questions
- Typed Error Channels4 questions
- Actions As Values4 questions
- Traverse & Invert5 questions
questions
214 · 11 sectionsA function that decides whether a subscription renews reads the clock itself — what does that cost you?
basics
~20 sReading the clock inside the decision turns time into an undeclared input: the same subscription record can produce two different answers, and no test can pin the answer down. Pass the instant in as a parameter.
A trip planner caches each route-distance result by its pair of stops - why does that cache never go stale?
basics
~20 sA pure lookup returns the same distance for the same pair of stops every time, so a stored result can never disagree with a fresh call. Invalidation exists to catch drift between cache and source; purity removes the drift.
Two pure cell formulas in a recalculation pass — when may the engine evaluate them in either order?
basics
~20 sEither order is safe exactly when neither formula reads the cell the other writes. Purity closes every other channel — no shared counters, no clock, no files — so only declared data dependencies constrain the schedule.
A delivery-fee function reads the clock for a cut-off, bumps a metrics counter and writes a log line — which of those make it impure?
basics
~20 sA clock read, a counter bump and a log write are all side effects. Purity needs two things at once: the result depends only on the arguments, and the call changes nothing observable outside itself.
In a payroll calculation, what does it mean that a call may be replaced by its result?
basics
~10 sIt means the call is referentially transparent: anywhere the call appears, writing the value it returned instead leaves the meaning of the program unchanged, and putting the call back is equally safe.
A document editor keeps past versions in a persistent structure - after an edit, what can an old reference still do?
basics
~20 sA persistent structure never destroys the version an update was applied to. The edit returns a new version, and a reference taken earlier still reads exactly the document it read before, with no copy having been taken.
A price list's tier-row field is never reassigned, yet a reader sees a row's amount change — why?
basics
~20 sFixing a field fixes the binding, not the object it points at. The tier rows are separate objects; anyone holding a reference to one can write its amount in place, without ever touching the price list's field.
Every request handler reads one shared routing-table value that is never modified after it is built; why does no handler need a lock?
basics
~20 sA value that never changes has no intermediate state to hide, so concurrent readers have nothing to exclude each other from. Locks guard transitions, and this value makes exactly one: from unbuilt to finished, before anybody can see it.
An immutable tree-shaped value gets one leaf replaced — why is the new version not a full copy?
basics
~20 sOnly the nodes along the route from the root to the changed leaf are rebuilt; every subtree the change never touched is pointed at by both versions. The copy is one path deep, not one tree wide.
A cart line is immutable — how do you produce one with a new quantity, and what happens to the original?
basics
~10 sYou build a new line that copies every field except quantity, which takes the new value. The original object is untouched, so any code already holding it keeps seeing the old quantity.
In an editor that keeps each shortcut's behaviour in a map entry, what does that require of functions?
basics
~20 sFunctions have to be ordinary values: storable in a variable, a field or a map entry, and passable and returnable like a number. The table then holds behaviour itself rather than a code that some conditional must interpret.
A shortcut table is filled with table[key] = save(), and the document saves at startup - what was stored?
basics
~20 sThe entry stored the result of calling save, not the function itself: the parentheses invoked it while the table was being built. Writing table[key] = save stores the function value and leaves the call for key-press time.
A chat client hands a send routine a function to run on acknowledgement — what control has the caller given up?
basics
~20 sThe caller gives up control of invocation: the send routine now decides whether the acknowledgement handler runs at all, when it runs, how many times, with what arguments, and on which execution context. That reversal is inversion of control.
A renderer declares its cell-formatter hook as one value in, one display string out — what does that declaration constrain?
basics
~20 sA function type fixes arity plus what stands in each parameter position and in the result position. A supplied formatter must accept the one argument the renderer will pass and hand back something the renderer can print.
What must a callback-shaped API document about the handler it accepts, beyond that handler's parameters?
basics
~20 sThe invocation contract: how many times the handler may run, whether it can run before the registering call returns, on which execution context, how failures are delivered to it, and what the routine does if the handler itself throws.
If a mapping step returns a list of sessions per course, what shape does the pipeline hold before flattening?
basics
~20 sA list of lists: one inner list of sessions per course, in course order, with an empty inner list wherever a course scheduled nothing. Flattening one level concatenates those inner lists into a single list of sessions.
In a fold that collapses a day of till transactions into one totals record, what do the seed and the combining step each contribute?
basics
~20 sThe seed is the accumulator's starting value and the result returned for an empty day. The combining step takes the accumulator so far plus one transaction and returns the next accumulator. A fold threads that accumulator through every element.
In an order pipeline, how do a mapping stage and a filtering stage each change a collection's size and order?
basics
~20 sA mapping stage produces exactly one output per input, so count and order are unchanged and only element values or types change. A filtering stage keeps a subset in the original relative order, so its count can only shrink.
A timing wrapper takes a remote-lookup function and returns a new function — why must the returned function keep the original's signature?
basics
~20 sKeeping the same parameters and return type makes the wrapper a drop-in: every existing call site keeps working, the wrapper goes in and comes out at one wiring line, and other wrappers can stack around it.
What distinguishes currying a three-argument permission check from partially applying its role argument?
basics
~20 sCurrying reshapes the check itself into a chain of one-argument functions, one per parameter, and mentions no argument values. Partial application supplies actual values for some parameters and hands back a single function that still needs the rest.
In a right-to-left composition written compose(compress, watermark, resize), which stage sees the raw photo first?
basics
~10 sResize sees it first. A right-to-left combinator applies the last-listed function to the argument and feeds each result leftwards, so the stages are listed in the reverse of the order they run.
In a job-board candidate filter, what does an 'and' combinator over two named predicates return - a boolean or another predicate?
basics
~10 sAnother predicate. A combinator builds a condition rather than evaluating one: nothing is tested until the assembled predicate is finally applied to a candidate, and then it asks both questions and combines the answers.
Why can the stages of a composed text-normalisation pipeline be regrouped freely but never swapped with each other?
basics
~20 sComposition is associative but not commutative. Regrouping only changes which adjacent stages get bracketed together, so every stage still receives the same input; swapping two changes what the later one is handed, and therefore the result.
How do you build a result ordering like 'best match first, then earliest application' from small comparators rather than one comparison function?
basics
~20 sGive each comparator one key and one direction, then chain them: the next comparator is consulted only when the previous one reports a tie. Each returns a three-way verdict - before, equal, after - and 'equal' is the signal that hands control to the tiebreaker.
In a composed text-normalisation pipeline, when is a stage that returns its input unchanged genuinely useful?
basics
~20 sA stage returning its input unchanged is the identity function: composing it changes nothing. It earns its place as a neutral default - what an optional stage falls back to, and the starting point when a list of stages is combined into one.
What makes a construct an expression rather than a statement, and why can only one of them nest inside another?
basics
~20 sAn expression evaluates to a value, so it can sit anywhere a value is expected and nest inside a larger expression. A statement is run for its effect and denotes nothing, so nothing can be built around it.
A nightly sales rollup states the totals it wants instead of spelling out the steps - what does that hand the runtime?
basics
~20 sStating the result instead of the steps hands the runtime the choice of strategy: the order of the work, chunk sizes, placement and passes. You describe what; it decides how, and can change how without you editing anything.
When an access-log summary loop is rewritten as a chain of transformation stages, what bookkeeping does the chain remove?
basics
~20 sThe chain removes the index and its bounds, the mutable counter and its half-built intermediate states, and the advance-and-test mechanism; what is left is three named stages - keep the well-formed lines, take each status, count them.
A validation scan stops at the first bad record; what does an eagerly evaluated transformation chain do differently?
basics
~20 sAn eagerly evaluated chain runs every stage over every record before anything is picked out, so it pays for the whole batch even when the answer was settled at record three. The loop returns the moment it knows.
A routine declares an empty band variable and assigns it in each branch below — what change removes that variable?
basics
~20 sMake the choice itself denote a value and bind the result once: each arm yields a band instead of assigning one. The name is then initialised at its declaration, never reassigned, and no path can leave it empty.
A retry helper builds a never-ending sequence of back-off delays, then takes the first five - why does building it not run forever?
basics
~20 sAn unbounded sequence is a recipe, not a collection: a seed plus a rule for the next element, computed only when something demands it. Building it stores the rule; taking five runs the rule four times.
In a short-circuiting boolean AND, what does the left operand let you guard against?
basics
~20 sA short-circuiting AND evaluates its right operand only when the left one is true, so the left test can establish the precondition the right test needs - presence, a non-empty collection, a valid index - instead of hoping it holds.
Under call-by-value, when is an expensive diagnostic argument evaluated if the check that receives it usually ignores it?
basics
~10 sCall-by-value evaluates every argument at the call site, before the check's body starts. The expensive diagnostic is built on every call, including the calls where the check discards it unused.
A lazy value holds a thunk: a computation not yet run. What happens the first time it is forced, and every time after?
basics
~20 sForcing runs the stored computation once: the thunk evaluates its body, replaces itself with the resulting value and returns it. Every later force returns that cached value without running anything, so the work is paid for at most once.
An eager three-stage chain over a large export versus the same stages run lazily - what differs in traversals and memory?
basics
~20 sThe eager chain walks the data once per stage and builds a complete collection at every stage boundary. The fused lazy chain walks it once and holds one element in flight, so no collection exists at a boundary - only the final result is built.
When a function that captured local values is stored and called long after its defining call returned, what keeps those values alive?
basics
~20 sThe stored function itself keeps them alive. Captured state is moved out of the short-lived call frame into storage the function value owns, so it lives exactly as long as that function value stays reachable.
A queued audit entry captures a status variable that its caller later reassigns — what decides which value the entry reports?
basics
~20 sCapture mode decides. A by-value capture copied the status when the entry was created and reports the old value; a capture of the binding itself reads the variable at call time and reports the new one.
Under lexical scoping, where is a free name inside a message-formatting helper looked up?
basics
~20 sLexical scoping resolves a free name in the text surrounding the helper's definition - innermost enclosing region first, then outward. The call site is never consulted, so the helper means the same thing everywhere it is used.
A batch job's sequence issuer keeps its counter in a variable captured by the returned function: why does that counter survive between calls?
basics
~20 sThe returned function captured that variable, so the variable lives as long as the function value does instead of ending with the call that created it. No code outside can name it, so the returned function is the only way in.
In a nested comment thread, how does the shape of the data decide a recursive walker's cases?
basics
~20 sThe data definition lists the ways a value can be built, and each way becomes one case. A reply list is either empty or a comment plus the rest, so the walker has exactly those two cases.
When you rewrite a recursion into accumulator-passing style, where does the work that followed the recursive call go?
basics
~20 sIt moves across the call, into the argument expression. The combining step is applied to the running result first, and the updated value is passed down, so the recursive call becomes the last thing the function does.
A corecursive appointment-date producer has no base case, so what condition makes it well defined instead of a definition that spins?
basics
~20 sGuardedness: every recursive call must sit behind a step that has already handed back one date, so each turn of the definition delivers output. A path that recurses without delivering anything is the real failure to look for.
Both phase functions of a turn loop end in a tail call to each other — why does the usual tail-call-to-loop rewrite not fire?
basics
~20 sThat rewrite is local: it turns a call to the enclosing function into reassigned parameters and a jump to the top of the same body. A mutual call's target is a different body, so no such jump exists.
Why does a comment walker that recurses only into each comment's own replies need no depth counter?
basics
~20 sEvery recursive call receives a part of the value it was given, so the argument is strictly smaller each time and the descent runs out of thread. Termination comes from the data, not from a number someone picked.
In a match expression, what does binding a value's parts inside the pattern give you that reading fields in the branch body does not?
basics
~20 sBinding in the pattern makes one construct do two jobs: it tests that the value has the case's shape and names the parts of that shape at the same time. The branch body starts with named parts instead of shape checks and accessor calls.
What does a compiler's exhaustiveness check on a match over ticket states actually guarantee?
basics
~20 sAn exhaustiveness check proves at compile time that a match handles every variant its type can hold. The payoff comes later: add a variant and every match with a gap fails the build instead of falling through at run time.
In algebraic data types, what distinguishes a payment that is exactly one of card, transfer or credit from a record holding all three?
basics
~20 sA sum type holds exactly one of its variants, tagged so you can tell which; a product type holds a value of every component at once. The payment is a sum, the record of all three is a product.
When a match case's pattern fits but its guard condition evaluates to false, what does the match do next?
basics
~20 sThe case is rejected as a whole and matching continues with the cases below it, exactly as if the pattern had not fitted. A failed guard is not an error and does not abort the match.
A match over ticket states ends with a catch-all case — what does that cost when a new state is added?
basics
~20 sA catch-all makes the match total for every variant, including ones that do not exist yet, so the compiler reports nothing when a state is added. The new state silently takes a branch written for the variants of a year ago.
A transfer function returns either a success value or a described failure. What does that return type force on every caller?
basics
~20 sEvery caller must handle the failure case to reach the success value, because the two outcomes are alternative shapes of one returned value. A failure that is thrown instead puts no such obligation at the call site.
A directory lookup returns an empty-or-one-employee context instead of a null reference: what does that force every caller to do?
basics
~20 sAn empty-or-one-value context puts the missing case in the return type, so a caller cannot reach the employee without opening the container: transform the value inside it, or supply a fallback. The possibility of nothing stops being a convention.
An installer builds a value describing fetch, unpack and link instead of performing them — what does that buy the calling code?
basics
~20 sBuilding the plan performs no effects, so the code that assembles fetch, unpack and link can be tested, inspected, combined and rewritten as ordinary data. Nothing reaches the network or the disk until a runner is handed the value.
A transfer request is validated for amount, currency and destination account — what changes when the response must list every bad field?
basics
~20 sThe three checks stop being a chain and become independent parts combined together. A chain abandons the remaining checks at the first failure; combining runs all three and merges their failures into one description the response can render.
When you map a lookup that itself returns a wrapped value over a wrapped value, what comes back, and which operation did you actually need?
basics
~20 sMapping a function that itself returns a wrapped value gives you a wrapper nested inside a wrapper. Chaining is the operation you needed: it runs the step and hands back one layer, absorbing the step's own context instead of stacking it.