skip to content

Why can a relay body that carries any caller-chosen payload type not branch on which type it was handed?

level: middleimportance: must knowfreq 55%

answer

  1. the promise was made over all of them
  2. no declared capability, nothing to call
  3. moving is allowed, looking is not
  4. the caller may send the operation along
  5. some platforms keep no record to test

basics

~20 s

Because the signature promised the same behaviour for every type a caller may name, and behaviour that depends on the choice makes that promise false for the rest. An unconstrained placeholder also declares no capability, so there is usually nothing to branch on.

solid answer

~50 s

Two reasons reinforce each other. The first is the promise: the signature says *for every type the caller names*, so a body that special-cases one choice is quietly making a weaker claim, and every call site that named some other type was told something that is no longer true. The second is mechanical: an unconstrained placeholder declares no capabilities, so there is nothing the body can legitimately call on the value to find out what it is. On platforms that discard the record of the type argument once it has been checked, there is not even a record left to test at run time; on platforms that keep it, testing it is possible and still breaks the promise. What remains available is generous: the body may move, store, drop, duplicate or reorder the payload, hand it back, or pass it to an operation the caller supplied alongside it.

code

pseudocode · 8 lines
pseudocode
function groupPayloads<T>(payloads: list of T,
                          keyOf: function(T) returns Key) returns list of T
    buckets = empty multimap
    for each p in payloads
        k = keyOf(p)              // allowed: the caller supplied this operation
        buckets.add(k, p)         // allowed: move, group, duplicate, reorder
    return buckets.flattenInKeyOrder()
    // nowhere does the body ask p what type it is

go deeper

for a junior

Recall that a payload whose type is an open placeholder can be passed around but not questioned, and that anything type-specific has to be handed in by the caller.

for a middle

Explain both halves: the signature promised uniform behaviour over every choice, and the placeholder declares no capability, so there is nothing legitimate to branch on.

for a senior

Show the consequence in a codebase: the per-type knowledge lives with the caller that owns the type, which is why the relay stops needing an edit every time a new payload appears.

for a principal

Weigh what the restriction is worth. It is the reason a shared body can be published to teams you will never meet, and the first special case is the moment that stops being true.

## The promise is the reason, not the tooling A signature that quantifies over every payload type is a claim about **all** of them at once. If the body inspects the caller's choice and does something different for one of them, the claim is no longer true of the others — not because a compiler objected, but because the sentence the caller read was *for every type, this relay behaves like so*, and it now behaves like two different things. The restriction would still be a defect on a platform that made the branch easy to write. This is worth separating from the tooling argument, because candidates usually reach for the tooling first and then have nothing to say when the tooling would have allowed it. ## What the body may still do The restriction is narrower than it first sounds. With a payload whose type is an unconstrained placeholder, the body may: - **Move** it — accept it here, hand it back there, put it in a field, pass it to another relay. - **Store** it, for as long as it likes, and return it later. - **Drop** it, if the signature permits a result without it. - **Duplicate** it — the same value handed to two places is still the same value. - **Reorder** several of them, group them, reverse them, interleave them with others of the same type. - **Hand it to an operation the caller supplied** along with the payload — this is the escape hatch, and it does not break anything. That last bullet is the one people get wrong in both directions. Passing the payload to a caller-supplied operation is not cheating: the capability arrived **from the caller**, with the value, so using it needs nothing the signature failed to promise. The body still knows nothing about the type; it is simply running code the caller chose to send along. ## What the body may not do 1. **Ask the payload what type it is** and take a different path accordingly. 2. **Call an operation the placeholder never declared** — there is no stated capability, so there is nothing to call. 3. **Build a fresh value of that type** out of nothing; the signature handed over no way to make one. 4. **Assume anything about representation** — size, layout, whether two payloads can be ordered or compared. ## Compile time, run time, and what survives Platforms genuinely differ on what happens to the caller's choice after it has been checked. Some **discard** it once the check passes, leaving the running value with no record of which type was named — erasure. Others **keep** it available while the program runs — reification. The direction matters and is easy to state backwards: erasure loses information at compile time, reification preserves it for run time. | Situation | Can the body test the choice? | Should it? | |---|---|---| | The record is discarded after checking | No — nothing is left to test | The question does not arise | | The record survives to run time | Yes, mechanically | No — the promise still forbids it | | The caller supplied an operation | It does not need to test | Yes, that is what the operation is for | So "it is impossible" is the wrong sentence to say in an interview. The honest sentence is: on some platforms it is impossible, and where it is possible it is still a broken promise. ## The shape this pushes code into Because the body cannot look, the per-type knowledge has to live with whoever owns the type, and it reaches the relay as an argument. That is a genuinely good arrangement: the relay stays small and stable, each caller keeps the knowledge only it has, and a new payload type arrives with its own operation instead of requiring an edit to shared code. The restriction is what makes the relay reusable in the first place — a body that could look at the payload would eventually be edited every time a new caller appeared.

  • If the body may not look at the payload, how does a relay ever do something type-specific?
    The caller sends the type-specific step in with the payload, as an operation the body simply calls. The knowledge stays with the side that owns the type, and the relay keeps a signature that is still true for every other caller. A declared limit on the placeholder is the other route, but that is a different, narrower promise.
  • Is the restriction weaker on a platform that keeps the type argument available at run time?
    Mechanically yes, contractually no. Where the record survives, the body can test it; doing so still makes behaviour depend on a choice the signature promised to be indifferent to, so every caller that named a different type was misled. The restriction is about the claim, not about what the platform lets you type.

A courier who moves sealed bags can reroute them, hold them, hand one to a named agent, even duplicate a manifest — everything except open one. The seal is what lets the same courier serve every sender.

saying these in an interview costs you the question

  • Assumes the body may call arbitrary operations on an unconstrained placeholder
  • Believes a run-time type test is always available inside such a body
  • Thinks supplying an operation alongside the payload breaks the promise
  • Says the body may not move, drop or duplicate the payload either
  • Treats "do not branch on the type" as a style rule rather than the contract
  • Claims the body can construct a fresh value of the caller's chosen type