Generic Programming
Code written once against a placeholder type instead of a concrete one: parameters, bounds, variance, erasure and specialization. Interviewers use a generic signature to test type-system depth.
on this pageshowhide
explore
- Type Parameters16 questions
- Reuse Without Casting4 questions
- Function-Level vs Class-Level4 questions
- Inference and Its Limits4 questions
- Multiple Placeholders4 questions
- Constraints and Bounds16 questions
- Supertype and Subtype Limits4 questions
- Intersection Requirements4 questions
- Self-Referential Types4 questions
- Structural vs Nominal Matching4 questions
- Variance18 questions
- Substitutability Question5 questions
- Producer and Consumer Positions5 questions
- Declaration-Site vs Use-Site4 questions
- Mutable Containers and Arrays4 questions
- Type Erasure and Reification16 questions
- What Survives Compilation4 questions
- Forbidden Runtime Operations4 questions
- Tokens and Witnesses4 questions
- Compatibility and Metadata Cost4 questions
- Specialization16 questions
- Monomorphization vs Shared Code4 questions
- Per-Type Implementations4 questions
- Primitive and Value Layouts4 questions
- Code Size vs Speed4 questions
- Parametric Polymorphism13 questions
- Universal Quantification4 questions
- Parametricity and Free Theorems5 questions
- Higher-Kinded Abstraction4 questions
questions
95 · 6 sectionsA connection type carries a message-type placeholder and a standalone helper carries its own - how long is each fixed?
basics
~20 sA placeholder introduced on the type is chosen once when an instance is created and every member of that instance shares it. A placeholder introduced on one operation is chosen afresh for each call and is scoped to that call.
When a generic filter helper is called without an explicit type argument, what does the compiler use to fill its placeholder?
basics
~20 sThe compiler solves the placeholder from evidence at the call site: the static types of the arguments, the type the result is expected to have where the call sits, and any bound or default declared on the parameter itself.
Why is a routing registry that maps route keys to handler payloads declared with two placeholders rather than one?
basics
~20 sA key type and a payload type vary independently, so each needs its own placeholder. A placeholder takes one type argument per use, so a single placeholder would force every route key and its payload to be the same type.
A lost-property desk stores every item as one universal supertype and casts on collection — what does a type parameter buy instead?
basics
~20 sA type parameter moves the wrong-kind failure from collection time to the deposit call, and from run time to compile time. Callers also get items back already typed, so no downcast on retrieval can fail.
An unrestricted placeholder type in a payroll totalling routine — what may its body do with each element?
basics
~20 sOnly what every type supports: hold it, pass it on, return it. With no limit written, the placeholder is implicitly pinned at the widest type available, so no pay-component operation such as reading an amount is callable on it.
A board routine needs its departure placeholder both time-orderable and fixed-width renderable - why two requirements instead of one supertype?
basics
~20 sListing two requirements on one placeholder admits any type that already orders and already renders, unchanged. A combined supertype forces every candidate to be redeclared under it, which is impossible for types you do not own, and welds two unrelated capabilities together.
In a tournament ladder, why limit the entrant placeholder in terms of itself rather than to a plain comparable supertype?
basics
~20 sA self-referential limit types each entrant's comparison against its own type, so comparing two unrelated kinds of entrant fails to compile. A plain comparable supertype only promises comparison with some entrant, leaving the mismatch to surface at run time.
Requiring an add-in to implement a published interface versus requiring only the operations you call — what does each demand of the add-in team?
basics
~20 sNaming a supertype demands that the add-in team edit their type's declaration and take a build dependency on your published artifact. Requiring a shape demands only that the members exist, so a type written before your host can qualify unchanged.
What decides whether a payroll routine's placeholder is pinned at or above a type rather than at or below it?
basics
~20 sWhat the body does with the value. If it calls that type's own operations, pin the placeholder at or below the type; if it only hands values of that type in somewhere, pin it at or above.
An array of premium seats is passed where an array of seats is expected, then a standard seat is stored - why is that unsafe?
basics
~20 sThe array's real element type is still premium-only, so the store plants a value the original holder's type promises can never be there, and the next read of that slot returns something the reader believes is impossible.
Reading a row source declaration, what makes its placeholder a producer rather than a consumer?
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.
Electric vans are vehicles. Does it follow that a roster of electric vans is a roster of vehicles?
basics
~20 sNot automatically. A subtype relation between two element types says nothing on its own about the containers over them; the container type decides. Three answers are possible - covariant, contravariant, invariant - and invariance is the usual default.
A shared policy library can state a type's variance once on the declaration or let each caller narrow it - what does each buy?
basics
~20 sDeclaration-site states the direction once, so every use gets the subtyping free - but only for a type whose parameter sits in one direction. Use-site keeps the type invariant and lets each signature narrow its own view, repeatedly.
In a language design where an array of a subtype is an array of its supertype, what must happen on every store?
basics
~20 sEach store is tested at run time against the element type the array was actually created with, and a value that does not fit is rejected right there, at the write - not at the call that let the widened array through.
Why does a language that discards type arguments reject a run-time test asking whether a value is a stack of text edits?
basics
~20 sA run-time test can only read what the value still carries, and the discarded argument is not there. Every stack of edits shares one run-time shape, so the test cannot be decided, and the compiler rejects it instead of guessing.
A settings loader is handed the expected type as an argument alongside the key — what does it do with that argument?
basics
~20 sThe type argument is the evidence the run time no longer carries: the loader uses it to choose how to convert the stored text and to check the converted value before returning it, so a mismatch fails at the read.
After compilation on a platform that discards type arguments, what does a stored container value still record about its argument?
basics
~20 sNothing you can ask the value for. The argument is spent during compilation, and what runs is an ordinary container over the erased element shape. Only a declaration, never the object it points at, may keep a written description of it.
Why would a platform add parameterized types by checking the arguments and then discarding them from the compiled artifact?
basics
~20 sDiscarding keeps the compiled shape identical to the pre-parameterized one: one body serves every instantiation, the existing run-time system needs no change, and code compiled before the feature can still call, and be called by, parameterized code.
Inside a generic undo stack, why can the body not construct a fresh value of its own type placeholder?
basics
~20 sConstruction has to name a concrete type at the point it happens: what to allocate and how to initialise it. A placeholder names none of that once the argument is discarded, and nothing promises the argument can be built at all.
A decoder body is written once against a placeholder sample type: which two strategies can a toolchain use to make it run?
basics
~20 sA toolchain can generate a separate copy of the body for each type argument, with every operation resolved to a direct call, or compile one shared copy that receives the type's operations as a passed-in table and calls them indirectly.
An archiver's generic record writer also ships a hand-written body for one chosen type argument - what decides which body runs?
basics
~20 sThe most specific matching definition wins. Where the type argument becomes known, every candidate whose pattern that argument satisfies is gathered, and the narrowest is selected; the general body is the fallback for arguments nothing narrower claims.
A tile renderer stores coordinate points in a generic container; why does each point cost an allocation and an extra memory hop?
basics
~20 sA shared container body is compiled once against one uniform slot shape — a reference — so an unboxed point cannot sit in the buffer directly. Each point becomes a separate heap object and the slot holds that object's address.
Compiling a separate body for each type argument makes calls faster - what does the build pay for that speed?
basics
~20 sPer-argument body generation costs build time (a fresh compile per distinct argument), artifact size (every generated body ships), and instruction-cache pressure (more distinct code competing for the same cache). The gain is a direct, inlinable call with no run-time indirection.
In your archiver, the hand-written body for one record type drifted from the general body - why does the bug appear only at some call sites?
basics
~20 sBecause which body runs is decided by what each call site statically knew about the type, not by the value. A site that fixed the argument takes the drifted body; a site still holding it generically can keep running the general one.
A routine takes one value of an unknown type and returns a value of that same type — what can its body return?
basics
~20 sOnly the value it was handed. A body that can neither inspect nor construct a value of an unknown type has no other value of that type in scope, so the one body that returns a result returns its argument.
Why can a relay body that carries any caller-chosen payload type not branch on which type it was handed?
basics
~20 sBecause 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.
In a relay that carries a payload of any type the caller names, who fixes that type, and when?
basics
~20 sThe caller fixes it, independently at every call site. A universally quantified signature promises the same behaviour for every type a caller may name, so the implementation is written once and has to hold for all of them.
A validation helper must work for any result wrapper rather than any element type — what must its type parameter itself accept?
basics
~20 sIts type parameter must itself take a type argument: it stands for a wrapper, not for a finished type. Such a parameter is higher-kinded, and its kind records how many arguments it still expects before it names a type.
When a type system's placeholders cannot stand for a wrapper itself, what do teams write instead of one pipeline per wrapper?
basics
~20 sThey normalise every stage result onto one chosen wrapper at the boundary, encode the wrapper as a marker type with lift and lower conversions, or generate the per-wrapper copies from a single source. Each trades the missing abstraction for a different cost.