Reading a row source declaration, what makes its placeholder a producer rather than a consumer?
answer
- follow the direction of the data
- check every member, not one
- results are out, parameters are in
- output-only means producer position
- both sides means neither
basics
~20 sA placeholder is a producer when every member that mentions it hands a value of it back: it appears only as output — a return type or a readable property — and never as a parameter the caller supplies.
solid answer
~40 sGo through the declaration member by member and ask, for each mention of the placeholder `T`, whether a value of `T` is coming out of the type or going into it. If every mention is a result — a return type, a property you can only read — the placeholder sits in a producer position: the type only ever hands `T` values to you. If every mention is an incoming parameter, it sits in a consumer position: you only ever hand `T` values to the type. The mnemonic in neutral words is the direction of travel — you only read from a producer, you only write to a consumer. A placeholder that appears on both sides in the same type is neither, and it is pinned in place.
code
pseudocode · 10 linestype RowSource<T>:
function nextRow() returns T // T comes out -> producer position
function hasMore() returns boolean // no mention of T -> irrelevant
type LineSink<T>:
function accept(value T) // T goes in -> consumer position
type RowBuffer<T>:
function take() returns T // T comes out
function put(value T) // T goes in -> both sides: pinnedgo deeper
Recall the reading rule: look at every member that mentions the placeholder and ask whether a value of it comes out or goes in. Output only means producer, input only means consumer.
Be able to walk a declaration line by line out loud and name the exact mention that decided the classification, including a property that can be both read and written.
In review, spot the single member that quietly puts the placeholder on the other side and turns a type everyone was treating as read-only into one that is pinned.
Decide whether shared types in the codebase are deliberately kept single-directional, because that one choice is what gives every downstream caller room to pass a different argument later.
## What a position actually is A generic type is written against a placeholder — call it `T` — that its author never names. Before you can say anything about substituting one instantiation for another, you have to answer something simpler: **which way does a `T` value travel across this type's boundary?** That direction is what *position* means. A `T` that only ever travels outward, from the type to the caller, sits in a **producer position**. A `T` that only ever travels inward, from the caller into the type, sits in a **consumer position**. Nothing about the type's name, its documentation, or what the author had in mind decides this. Only its member signatures do. ## Reading the declaration member by member The procedure is mechanical, and an interviewer expects to watch you run it out loud: 1. List every member whose signature mentions `T` at all — methods, readable properties, writable properties, and any construction parameter that survives as a member. 2. For each mention, ask **who supplies the value**. If the type supplies it and the caller receives it — a return type, a readable property, an element yielded by an iteration member — that mention is an **output**. 3. If the caller supplies it and the type receives it — a method parameter, the incoming value of a writable property — that mention is an **input**. 4. Total them. All outputs means producer. All inputs means consumer. **A mix means neither**, and the placeholder is pinned. The nightly report renderer makes this concrete. Its row source declares `nextRow() returns T` and `hasMore() returns boolean`: exactly one mention of `T`, and it is an output, so that placeholder is a producer. Its line sink declares `accept(value T)`: one mention, an input, so that placeholder is a consumer. A buffer that declared both `take() returns T` and `put(value T)` would have one of each and would be pinned. ## The two clean cases | what the members show | position | the type's role | the mnemonic | |---|---|---|---| | every mention of `T` is a result | producer | it hands `T` values out | you only read from it | | every mention of `T` is a parameter | consumer | it takes `T` values in | you only write to it | | some of each | neither | it does both | it is pinned in place | The mnemonic is worth carrying in exactly those words, because it survives translation into any type system: **you only read from a producer, you only write to a consumer**. Candidates who memorised a keyword instead of the direction of travel get stuck the moment an interviewer writes a signature in a notation they have not seen before. ## The traps in the reading - **A parameter is a consumer position even though the caller "produces" the value.** The word *producer* describes the type being classified, not whoever created the value. This inversion is the most common wrong answer to this question. - **A writable property is two mentions in disguise.** Reading it is an output and writing it is an input, so a single read-write property puts `T` on both sides by itself. - **One stray member decides the whole classification.** A source with twenty reading members and one accepting member is not "mostly a producer"; it is a type with both positions. - **Members that never mention `T` are irrelevant.** A count, a flag, a close operation — none of them moves a `T` across the boundary, so none of them changes the reading. - **Nesting can flip the direction.** A parameter that is itself a function taking `T` is not straightforwardly an input, and the reading has to go one level deeper. - **Whether any annotation is written, and where, is a separate subject.** Position is a property of the signature that you can read before anyone annotates anything. ## Why this is the first step, not the last Position is the evidence; which substitutions are safe is the conclusion drawn from it, and the names for the three possible outcomes belong to the substitutability question rather than here. An interviewer who asks "why did you mark this one read-only?" is not asking for a rule you remember — they are asking you to point at the members. The answer they want sounds like: *every member that mentions the row type returns it, nothing takes one in, so this type only produces rows.* It also explains a great deal of library shape. A type that wants to stay flexible for its callers is deliberately kept single-directional, and authors split a read-only view away from a read-write one precisely so that the placeholder's mentions all stay on one side. When you meet a pair of types where one looks like a strict subset of the other, position analysis is usually the reason the pair exists at all.
- Where does a property that can be both read and written put the placeholder?On both sides at once. Reading it hands a value of the placeholder out and writing it takes one in, so a single read-write property is enough to give the type both positions. No single direction is then safe, however the rest of the members happen to read.
- Does a member that never mentions the placeholder affect the classification?No. Only mentions count. A source can declare any number of members over other types — a row count, a closed flag, a reset operation — without changing whether its placeholder is output-only, because none of them moves a value of that placeholder across the boundary.
- What do you call the position when the placeholder appears on both sides?Neither producer nor consumer. The placeholder is pinned: the type has to be treated as fixed in it, and at the declaration there is no direction in which one instantiation may stand in for another. Naming the three possible outcomes is the substitutability question's job.
A vending machine only ever hands things out; a post box only ever takes things in. You can tell which one you are standing at from the slot, not from the label on the front.
saying these in an interview costs you the question
- Says a parameter position is a producer because the caller produces the value
- Classifies from the type's name or its documentation instead of its members
- Calls a type a producer when only most of its members return the placeholder
- Thinks a read-write property puts the placeholder on just one side
- Believes the classification changes with the language rather than the signature