skip to content

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 pageshow

explore

questions

214 · 11 sections

A function that decides whether a subscription renews reads the clock itself — what does that cost you?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Reading 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.

open as a page

A trip planner caches each route-distance result by its pair of stops - why does that cache never go stale?

level: juniorimportance: must knowfreq 65%
basics
~20 s

A 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.

open as a page

Two pure cell formulas in a recalculation pass — when may the engine evaluate them in either order?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Either 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.

open as a page

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?

level: juniorimportance: must knowfreq 84%
basics
~20 s

A 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.

open as a page

In a payroll calculation, what does it mean that a call may be replaced by its result?

level: juniorimportance: must knowfreq 72%
basics
~10 s

It 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.

open as a page

A document editor keeps past versions in a persistent structure - after an edit, what can an old reference still do?

level: juniorimportance: must knowfreq 58%
basics
~20 s

A 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.

open as a page

A price list's tier-row field is never reassigned, yet a reader sees a row's amount change — why?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Fixing 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.

open as a page

Every request handler reads one shared routing-table value that is never modified after it is built; why does no handler need a lock?

level: juniorimportance: must knowfreq 60%
basics
~20 s

A 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.

open as a page

An immutable tree-shaped value gets one leaf replaced — why is the new version not a full copy?

level: juniorimportance: must knowfreq 64%
basics
~20 s

Only 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.

open as a page

A cart line is immutable — how do you produce one with a new quantity, and what happens to the original?

level: juniorimportance: must knowfreq 70%
basics
~10 s

You 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.

open as a page

In an editor that keeps each shortcut's behaviour in a map entry, what does that require of functions?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Functions 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.

open as a page

A shortcut table is filled with table[key] = save(), and the document saves at startup - what was stored?

level: juniorimportance: must knowfreq 74%
basics
~20 s

The 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.

open as a page

A chat client hands a send routine a function to run on acknowledgement — what control has the caller given up?

level: juniorimportance: must knowfreq 70%
basics
~20 s

The 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.

open as a page

A renderer declares its cell-formatter hook as one value in, one display string out — what does that declaration constrain?

level: juniorimportance: must knowfreq 68%
basics
~20 s

A 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.

open as a page

What must a callback-shaped API document about the handler it accepts, beyond that handler's parameters?

level: middleimportance: must knowfreq 56%
basics
~20 s

The 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.

open as a page

If a mapping step returns a list of sessions per course, what shape does the pipeline hold before flattening?

level: juniorimportance: must knowfreq 62%
basics
~20 s

A 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.

open as a page

In a fold that collapses a day of till transactions into one totals record, what do the seed and the combining step each contribute?

level: juniorimportance: must knowfreq 70%
basics
~20 s

The 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.

open as a page

In an order pipeline, how do a mapping stage and a filtering stage each change a collection's size and order?

level: juniorimportance: must knowfreq 76%
basics
~20 s

A 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.

open as a page

A timing wrapper takes a remote-lookup function and returns a new function — why must the returned function keep the original's signature?

level: juniorimportance: must knowfreq 65%
basics
~20 s

Keeping 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.

open as a page

What distinguishes currying a three-argument permission check from partially applying its role argument?

level: middleimportance: must knowfreq 66%
basics
~20 s

Currying 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.

open as a page

In a right-to-left composition written compose(compress, watermark, resize), which stage sees the raw photo first?

level: juniorimportance: must knowfreq 64%
basics
~10 s

Resize 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.

open as a page

In a job-board candidate filter, what does an 'and' combinator over two named predicates return - a boolean or another predicate?

level: juniorimportance: must knowfreq 62%
basics
~10 s

Another 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.

open as a page

Why can the stages of a composed text-normalisation pipeline be regrouped freely but never swapped with each other?

level: middleimportance: must knowfreq 64%
basics
~20 s

Composition 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.

open as a page

How do you build a result ordering like 'best match first, then earliest application' from small comparators rather than one comparison function?

level: middleimportance: must knowfreq 58%
basics
~20 s

Give 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.

open as a page

In a composed text-normalisation pipeline, when is a stage that returns its input unchanged genuinely useful?

level: juniorimportance: should knowfreq 50%
basics
~20 s

A 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.

open as a page

What makes a construct an expression rather than a statement, and why can only one of them nest inside another?

level: juniorimportance: must knowfreq 74%
basics
~20 s

An 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.

open as a page

A nightly sales rollup states the totals it wants instead of spelling out the steps - what does that hand the runtime?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Stating 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.

open as a page

When an access-log summary loop is rewritten as a chain of transformation stages, what bookkeeping does the chain remove?

level: juniorimportance: must knowfreq 72%
basics
~20 s

The 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.

open as a page

A validation scan stops at the first bad record; what does an eagerly evaluated transformation chain do differently?

level: juniorimportance: must knowfreq 68%
basics
~20 s

An 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.

open as a page

A routine declares an empty band variable and assigns it in each branch below — what change removes that variable?

level: middleimportance: must knowfreq 58%
basics
~20 s

Make 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.

open as a page

A retry helper builds a never-ending sequence of back-off delays, then takes the first five - why does building it not run forever?

level: juniorimportance: must knowfreq 62%
basics
~20 s

An 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.

open as a page

In a short-circuiting boolean AND, what does the left operand let you guard against?

level: juniorimportance: must knowfreq 74%
basics
~20 s

A 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.

open as a page

Under call-by-value, when is an expensive diagnostic argument evaluated if the check that receives it usually ignores it?

level: juniorimportance: must knowfreq 62%
basics
~10 s

Call-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.

open as a page

A lazy value holds a thunk: a computation not yet run. What happens the first time it is forced, and every time after?

level: juniorimportance: must knowfreq 58%
basics
~20 s

Forcing 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.

open as a page

An eager three-stage chain over a large export versus the same stages run lazily - what differs in traversals and memory?

level: middleimportance: must knowfreq 62%
basics
~20 s

The 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.

open as a page

When a function that captured local values is stored and called long after its defining call returned, what keeps those values alive?

level: juniorimportance: must knowfreq 58%
basics
~20 s

The 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.

open as a page

A queued audit entry captures a status variable that its caller later reassigns — what decides which value the entry reports?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Capture 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.

open as a page

Under lexical scoping, where is a free name inside a message-formatting helper looked up?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Lexical 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.

open as a page

A loop builds one action handler per table row; every handler later acts on the last row. Why?

level: juniorimportance: must knowfreq 74%
basics
~20 s

Every handler captured the same loop variable rather than a copy of that pass's value. The loop reassigned that one binding on each pass, so by the time any handler ran it read whatever the loop had left behind.

open as a page

A batch job's sequence issuer keeps its counter in a variable captured by the returned function: why does that counter survive between calls?

level: juniorimportance: must knowfreq 66%
basics
~20 s

The 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.

open as a page

In a nested comment thread, how does the shape of the data decide a recursive walker's cases?

level: juniorimportance: must knowfreq 72%
basics
~20 s

The 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.

open as a page

When you rewrite a recursion into accumulator-passing style, where does the work that followed the recursive call go?

level: middleimportance: must knowfreq 58%
basics
~20 s

It 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.

open as a page

A corecursive appointment-date producer has no base case, so what condition makes it well defined instead of a definition that spins?

level: middleimportance: must knowfreq 52%
basics
~20 s

Guardedness: 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.

open as a page

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?

level: middleimportance: must knowfreq 55%
basics
~20 s

That 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.

open as a page

Why does a comment walker that recurses only into each comment's own replies need no depth counter?

level: middleimportance: must knowfreq 58%
basics
~20 s

Every 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.

open as a page

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?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Binding 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.

open as a page

What does a compiler's exhaustiveness check on a match over ticket states actually guarantee?

level: juniorimportance: must knowfreq 62%
basics
~20 s

An 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.

open as a page

In algebraic data types, what distinguishes a payment that is exactly one of card, transfer or credit from a record holding all three?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A 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.

open as a page

When a match case's pattern fits but its guard condition evaluates to false, what does the match do next?

level: middleimportance: must knowfreq 55%
basics
~20 s

The 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.

open as a page

A match over ticket states ends with a catch-all case — what does that cost when a new state is added?

level: middleimportance: must knowfreq 66%
basics
~20 s

A 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.

open as a page

A transfer function returns either a success value or a described failure. What does that return type force on every caller?

level: juniorimportance: must knowfreq 66%
basics
~20 s

Every 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.

open as a page

A directory lookup returns an empty-or-one-employee context instead of a null reference: what does that force every caller to do?

level: juniorimportance: must knowfreq 78%
basics
~20 s

An 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.

open as a page

An installer builds a value describing fetch, unpack and link instead of performing them — what does that buy the calling code?

level: middleimportance: must knowfreq 58%
basics
~20 s

Building 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.

open as a page

A transfer request is validated for amount, currency and destination account — what changes when the response must list every bad field?

level: middleimportance: must knowfreq 58%
basics
~20 s

The 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.

open as a page

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?

level: middleimportance: must knowfreq 62%
basics
~20 s

Mapping 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.

open as a page