What may a payroll routine get back out of a destination whose element type is pinned at or above the ledger-line type?
answer
- acceptance, not content
- a write door, not a read door
- the body sees the limit, not the caller
- read back gives the widest type
- need both doors, take two parameters
basics
~20 sOnly what the widest type guarantees. Pinning at or above a named type promises every acceptable value is at least that general, so anything read back is some unknown supertype — the ledger line's own operations are not available on it.
solid answer
~40 sThe at-or-above direction is a promise about **acceptance**, not about content. It says the destination will take a ledger line, because its element type is the ledger-line type or something more general. That is enough to hand values in and nothing more: a value read back out is only known to be that unknown wider type, so the compiler falls back to what the widest type in the hierarchy declares. In practice a parameter pinned this way is a write door — useful for a posting routine that produces lines and never inspects them. If the routine also needs to read ledger lines, this limit cannot supply that; take a separate source parameter pinned at or below the ledger-line type, or name the element type exactly.
code
pseudocode · 6 linesfunction postTotals<S at or above LedgerLine>(sink: Sink of S, lines: list of LedgerLine)
for each line in lines
sink.accept(line) // legal: a LedgerLine is acceptable wherever an S is
var last = sink.mostRecent() // static type is S: some unknown wider type
log(last.costCentre()) // rejected: costCentre() belongs to LedgerLine, not to Sgo deeper
Remember the shape: a destination pinned at or above a type is somewhere to put values, not somewhere to get them. Anything you take back out is only known as the widest type.
Explain why: the routine is compiled once for every admissible caller, and the only thing all of those destinations have in common is the top of the hierarchy. That is what the compiler can safely offer.
Show the design consequence — a write-only parameter is what buys the routine every broader destination a team already owns — and reach for two parameters rather than one limit doing two jobs.
The trade to articulate is reach against convenience: exact element types make routines easy to write and narrow to adopt, while one-directional limits make shared utilities usable by teams whose sinks you have never seen.
## What the at-or-above limit actually promises When a placeholder is pinned **at or above** a named type, the declaration says: *whatever the caller supplies, it is that type or something more general.* For a posting routine whose destination is pinned at or above the ledger-line type, the admissible destinations include one that accepts ledger lines, one that accepts any posted record, and one that accepts absolutely anything. That promise is precisely strong enough for one thing: **handing a ledger line in**. Every admissible destination accepts ledger lines, because every one of them accepts something at least that general, and a ledger line is a ledger line. The body can post without knowing which destination it got. ## Why reading gives you only the widest type Now reverse the flow and ask what comes back out. The routine has no idea which of the admissible destinations it holds. If the caller supplied the most general one, values coming out of it could be anything at all — a posted record of another kind, something that is not a payroll artefact. Since the compiler must be sound for **every** admissible caller, it can only offer what all of them have in common, and what they have in common is the top of the hierarchy. So the type of a value read back is the unknown wider type, and the only operations available on it are those the widest type declares. Asking it for a ledger line's own operations is rejected — correctly, because for some legitimate caller that value genuinely would not be one. | Placeholder pinned | You may hand in | You may read back | Natural role | |---|---|---|---| | At or below the ledger-line type | Nothing of that type reliably | A value you may treat as a ledger line | A source to read from | | At or above the ledger-line type | Any ledger line | A value known only as the widest type | A destination to write into | | Exactly the ledger-line type | Any ledger line | A ledger line | Both, at the cost of admitting fewer callers | The table is the clean statement of the asymmetry this leaf is about: each direction opens one door and leaves the other shut, and naming the type exactly opens both but narrows who may call. ## Designing a signature around it - If the routine produces and posts, pin the destination at or above the produced type and take the widest possible set of callers. - If the routine consumes and inspects, pin the source at or below the type it inspects. - If it does both, take **two** parameters rather than trying to make one limit do both jobs. A payroll run is naturally that shape: components come in through one door, ledger lines go out through another. - If the routine really must read back what it wrote as the same type, name the element type exactly and accept the narrower caller set. That is a deliberate trade, not a failure. ## A common confusion People often assume that because the caller's destination *really is* a list of ledger lines, the values read back are ledger lines. They are — at run time. But the routine was not compiled against that caller; it was compiled against the limit, once, for all callers. The caller's extra knowledge stays on the caller's side of the boundary. This is the same rule that makes an unrestricted placeholder nearly powerless: the body sees the promise, never the argument. The corresponding second confusion is to think the limit somehow changes the caller's destination. It does not. The caller's collection keeps its own declared type, keeps its own contents, and behaves exactly as before. The limit only bounds what the **routine** is permitted to assume about it while it holds it. ## Why the restriction is worth having It is tempting to read all this as a loss — a parameter you cannot read from. It is better read as the price of a much larger caller set. A posting routine that names the element type exactly serves only destinations for that one type. The same routine pinned at or above the ledger-line type serves every more general destination a team might already have: a general ledger sink, an audit sink, a diagnostic collector that accepts anything. The routine gives up the read it was never going to perform, and gains every caller whose destination is broader than its own output. That is the judgment interviewers are probing with this question. A candidate who says "you can't read anything useful out of it" has the mechanism. A candidate who adds "and that is fine, because a routine that only writes should not be reading" has the design reason as well.
- What if the routine must both post ledger lines and read them back as ledger lines?Neither direction supplies both. Either name the element type exactly, accepting a narrower set of callers, or take two parameters — a destination pinned at or above the ledger-line type to write into, and a source pinned at or below it to read from. The two-parameter form usually reflects the design better anyway.
- Does the limit change the caller's own destination in any way?No. The caller's collection keeps its declared type and its contents; nothing about it is rewritten. The limit bounds only what the routine may assume while it holds the value, which is why the caller can still read its own contents at their real type after the call returns.
- What is actually gained by giving up the read?Callers. A destination pinned at or above the ledger-line type admits every more general sink a team already has — a general ledger, an audit trail, a catch-all collector — instead of only destinations for that one type. The routine surrenders a read it was never going to perform.
saying these in an interview costs you the question
- Expects a value read back through an at-or-above limit to carry the named type
- Believes the limit rewrites or narrows the caller's own destination
- Says a value handed in through an at-or-above limit may fail at run time
- Reads the restriction as a defect rather than the price of more callers