skip to content

A read's signature declares a placeholder and separately takes a class token — what breaks when the two disagree?

level: middleimportance: should knowfreq 40%

answer

  1. two sources of truth for one type
  2. the body only has the token
  3. unchecked hand-back after a passing narrow
  4. call site trusts the declared side
  5. type the token against the placeholder

basics

~20 s

Two untied statements of one type let the body narrow against the token and hand the value back as the placeholder through an unchecked step. Nothing fails at the read; the call site trusts the declared type and breaks at first use.

solid answer

~40 s

An untied signature states the type twice: once as the placeholder the call returns, once as the type the token stands for. The body can only honour one of them — it narrows against the **token**, because that is the evidence it actually has — and then returns the result as the placeholder through a step nobody checks. When the two agree this is invisible. When they disagree, the read succeeds: the text parsed, the narrow passed, the value came back typed as something it is not. The failure lands at the first operation that really needs the declared type, in code that never mentioned the store. The fix is not more checking but a tighter signature: declare the token parameter as evidence *for that placeholder*, so a contradicting call cannot be written.

code

pseudocode · 12 lines
pseudocode
# untied: the token says nothing about R
function read(key, token) returns R:
    value = converters.forType(token).parse(store.lookup(key))
    checked = token.checkedNarrow(value)     # passes: value really is the token's type
    return unchecked(checked as R)           # nobody checks this step

retries as WholeNumber = read("retries", tokenFor(Text))   # accepted; breaks at first use

# tied: the token is evidence for R itself
function read(key, token of R) returns R:
    value = converters.forType(token).parse(store.lookup(key))
    return token.checkedNarrow(value)        # the check IS the promise

go deeper

for a junior

The point to carry away: if a routine is told the type twice, in two independent ways, the two can disagree, and the one the body actually uses is the one it was handed as a value.

for a middle

Walk the body step by step — look up, parse, narrow against the token, hand back unchecked — and say exactly which step absorbs the disagreement and why no run-time check can catch it.

for a senior

Show the diagnosis: a narrowing failure in code far from configuration, traced back to a read whose evidence and promise were never tied. Then make the repair structural instead of adding assertions.

for a principal

The standard to set is that evidence parameters are typed against what they are evidence for, everywhere. It is a cheap rule that removes a whole class of far-from-cause failures, and it is easier to enforce at review time than any run-time check.

## One type, stated twice A read that takes evidence has two places where a type appears: the **placeholder** the signature returns, and the **type the token stands for**. Those are two independent statements unless the signature deliberately connects them. The moment they are independent, the API has two sources of truth for one fact, and the usual consequence of two sources of truth applies — they can drift, and the system cannot tell you which one was wrong. The duplication is also visible at the call site: the caller names the type twice, once by declaring what it expects and once by handing over the token. Reviewers sometimes call this ceremony and try to delete one. Deleting the wrong one is what produces the broken version. ## What the body actually does when they are untied The body has exactly one piece of run-time evidence — the token — so: 1. it looks up a conversion for the type the **token** names; 2. it parses the stored text with that conversion; 3. it narrows the produced value against the **token**, and that narrow succeeds, because the value really is of the token's type; 4. it returns the value as the **placeholder**, which it cannot check, through an unchecked step. Step 4 is where the disagreement is absorbed. Nothing in the read fails. The read cannot compare the placeholder with the token at run time, because the placeholder is precisely the thing that was discarded — that is why the token was passed in the first place. ## Where the failure lands The value now circulates typed as something it is not. The first operation that genuinely depends on the declared type fails: arithmetic on what turned out to be text, a field access on the wrong shape, a comparison that cannot run. The report names that operation and that line, and mentions neither the key, nor the store, nor the read. | | Untied token and placeholder | Token declared as evidence for the placeholder | |---|---|---| | A contradicting call | Compiles | Rejected by the compiler | | The narrow in the body | Follows the token and passes | Follows the token, which is the placeholder | | The hand-back to the caller | Unchecked, and wrong | Checked, because the types are the same one | | Where a mismatch shows | At first real use, far away | It cannot be written | | Cost of the guarantee | None, and none given | One tighter parameter type | ## Tying them in the signature The repair is structural. Declare the token parameter not as *a token of some type* but as *a token of the placeholder this call returns*. Then the compiler enforces the pairing at every call site: the evidence and the promise are the same type by construction, the body's narrow is a check of exactly the thing the caller was promised, and the unchecked step disappears because there is nothing left to assume. This is the general shape of the idiom, and it is worth stating as a rule: **evidence must be typed against the thing it is evidence for.** A token parameter that floats free of the signature it serves is decoration. ## The duplication that remains, and what to do with it Tying the two does not delete the repetition at the call site — the caller still writes the type and still passes a token. Options, in the order most codebases reach for them: - Let the call site's expected type fix the placeholder and take the token as the only spelling of it, so the type appears once. - Offer small pre-bound reads for the handful of types a settings store really carries, each closing over its own token, and keep the general form for the rest. - Leave the repetition alone where the general form is rare, because the tighter signature already makes drift impossible and the cost is a few characters. What not to do is keep the untied form and add a run-time assertion comparing the two. There is nothing to compare with: the placeholder is gone by then, which was the entire premise. ## The trap to name in an interview The convincing wrong answer is that the mismatch fails at the read, because "there is a check there". There is — against the token. A check only guarantees what it was given evidence for, and an untied signature deliberately gives it evidence for the wrong half.

  • Could the body simply compare the token against the placeholder at run time and reject the mismatch?
    No — there is nothing to compare it with. The placeholder is the argument the run time discarded, which is the reason a token is passed at all. The comparison has to happen in the compiler, which is what typing the token parameter against the placeholder arranges.
  • The token and the placeholder agree in every call today. Is the untied signature still a defect?
    Yes. It is an invariant held by convention across every call site, present and future, with no check anywhere and a failure mode that surfaces far from its cause. Tying the types costs one parameter declaration and converts a discipline that people must remember into one the compiler enforces.

saying these in an interview costs you the question

  • Assumes the compiler always ties a token to the placeholder
  • Says the body can compare the token with the placeholder at run time
  • Believes the mismatch fails at the read, where it is easy to see
  • Treats the duplication as harmless because both sides usually match
  • Proposes documenting the pairing rather than typing it