A counter closure holding private state and an object with a single increment method do the same job: how do the two forms differ?
answer
- two views of one idea
- state bundled with its behaviour
- count the operations you need
- a bundle of functions is an object
- named state can be inspected
basics
~20 sBarely, in substance: both bundle state with behaviour and both hide the state. They differ in surface — a closure offers exactly one entry point and state with no name, while the object gives the state a named home and room for more operations, plus something you can identify, inspect and describe.
solid answer
~50 sThey are the same construct seen from two sides: state plus behaviour, with the state reachable only through the behaviour. The closure is the minimal form — one operation, no type to declare, no name for the state — so it is the right shape when there really is one thing to do. The object is the form that scales: a second and third operation over the same state are just more methods, whereas the closure has to start returning a bundle of functions to keep up, which is an object with worse ergonomics. The object also has a name, a declared shape and a value you can describe in a log or a test; the closure's state has none of those, which is a feature when you want it hidden and a problem when you need to see it.
code
pseudocode · 21 lines# closure form: one operation, the state has no name outside
function makeIssuer(prefix):
counter = 0
function next():
counter = counter + 1
return prefix + counter
return next
# single-operation object form: the same state, now a named member
object Issuer(prefix):
field counter = 0
method next():
counter = counter + 1
return prefix + counter
# needing a second operation pushes the closure toward the object
function makeIssuerPair(prefix):
counter = 0
function next(): counter = counter + 1; return prefix + counter
function peek(): return prefix + counter
return bundle(next, peek) # an object, assembled by handgo deeper
Recall that both forms bundle state with behaviour and hide the state, and that the closure is the version with exactly one way in and no name for what it holds.
Explain the growth story: a second operation over the same state turns the closure into a bundle of functions sharing a binding, which is an object without a declared shape or name.
Argue the visibility trade in production terms — captured state cannot be logged, asserted on or dumped by name, so say which of the two you would ship in a long-running batch and what you would expose deliberately.
Set the convention rather than the instance: decide where in a codebase unnamed captured state is acceptable at all, given that it is invisible to tooling, and where state a team reasons about must carry a name.
## The two shapes Take the same job — hold a number, hand out the next one — and write it twice. As a **closure**: an outer function declares a counter, returns an inner function that advances and returns it. As a **single-operation object**: a type with one field for the counter and one method that advances and returns it. The behaviour is identical, and so is the hiding: in both, outside code reaches the number only by asking. This is the duality an interviewer is fishing for, and it has an old aphorism attached — that closures and objects are each other's poor-man's version. Each can be written in terms of the other. A closure is an object with exactly one operation; an object is a bundle of closures sharing one captured environment. ## Where they are genuinely the same - Both **bundle state with the behaviour that uses it**, so callers never manage the state themselves. - Both make the state **unreachable except through the surface offered**, whichever mechanism does the hiding. - Both give you **independence per construction**: a new closure or a new instance means a new, separate counter. - Both let you **swap the implementation** behind the same call shape, because the caller depends on how it is invoked, not on what is inside. ## Where they differ | | Closure form | Single-operation object form | |---|---|---| | Operations over the state | one, unless you return a bundle | as many methods as you need | | The state | a binding with no name outside | a named member of a declared shape | | Declaration cost | none beyond the function | a type to declare and name | | Discoverability | must read the factory | the type lists what it offers | | Identity and description | a function value, little to say about it | a value that can be named, described, compared | | Growing a second operation | restructure into a bundle | add a method | The row that decides most real cases is the first. The moment the sequence needs both "give me the next" and "tell me where you are without advancing", the closure must return two functions closing over the same binding — which is precisely an object, assembled by hand and without a name. If a third and fourth follow, the bundle is worse than the type would have been: the caller gets a grab-bag of function values with no declared shape. ## What the closure wins 1. **Nothing to declare.** When there is one operation, the closure is the entire design: no type, no name, no file. 2. **A tighter surface.** The state is not a member of anything, so there is nowhere to add a back door under pressure, and no accessor that a later change quietly widens. 3. **A value that travels as behaviour.** The result is directly the thing that gets passed, stored and called; there is no instance-then-method indirection at the call site. ## What the object wins 1. **Room to grow.** A second operation is an addition, not a restructuring. 2. **A vocabulary.** The type's name says what this is, and a reader sees the available operations in one place instead of reconstructing them from a return statement. 3. **Inspectability.** Named state can be described, compared, logged or asserted on. A captured binding has no name, so tests and diagnostics can only observe it through the behaviour — which is the same property that made it private, now working against you. ## How to choose 1. **Count the operations you will need over this state.** Exactly one, honestly, and for the foreseeable future: closure. Two or more: object. 2. **Ask who else must understand it.** State a whole team must reason about deserves a name; state that never escapes one function does not. 3. **Ask what has to be observable.** If an operator, a test or a log line must ever see the value directly, the closure form will fight you. 4. **Do not choose by paradigm loyalty.** Both hide state; neither is "the functional one" in any meaningful sense, since a closure carrying mutable state is not pure either. That last point is worth saying out loud in an interview. A captured mutable counter is hidden mutable state exactly as an instance field is. Calling the same function twice gives different results, so the closure form buys no reasoning benefit over the object form — the choice is about surface, growth and visibility, not about purity. ## What to say in the interview "They are the same idea with different ergonomics: state plus the behaviour that reaches it. The closure is the one-operation form with no name for its state; the object is the many-operation form with a named shape you can inspect. I pick the closure when there is exactly one thing to do and nobody needs to see inside, and the object as soon as there are two, or when the state must be describable."
- Does choosing the closure form make the counter functional or pure?No. A captured mutable counter is hidden mutable state exactly as a member is: two identical calls return different values, so the function is not pure and cannot be substituted by its result. The closure changes the surface, not the reasoning properties.
- A test needs to assert the issuer is at 42 without advancing it. Which form makes that easier, and why?The object, because the state has a name that a test can reach or that a read method can expose in an obvious place. With the closure you must have foreseen the need and returned a reading function; otherwise the only observation available is advancing the sequence, which changes what you are measuring.
- If a closure bundle and an object are equivalent, when is the bundle still the better answer?When the operations are few and genuinely local — built inside one function, consumed there, never named across a boundary. Declaring a type earns its keep when other code must refer to the thing; when nothing does, the bundle avoids naming something that has no readership.
saying these in an interview costs you the question
- Says the closure form is pure because there is no object involved.
- Claims the closure hides state while an object can never hide state.
- Thinks the object recomputes its value while the closure remembers it.
- Asserts a closure allocates nothing at run time because the variable is just a local.
- Treats the choice as a paradigm allegiance rather than a question of surface and growth.