skip to content

Metaprogramming

Code that inspects, emits or rewrites other code: run-time inspection, attached metadata, build-time emission, expansion and interception. Interviewers use it to turn framework magic into mechanism.

on this pageshow

explore

questions

96 · 6 sections

What does a type's run-time self-description contain that lets a renderer build one column per field of an unknown object?

level: juniorimportance: must knowfreq 68%
basics
~20 s

A loaded type hands back a structured description of itself: its members with their names and declared types, modifier and visibility flags, its supertypes and the contracts it implements, and the type's own run-time identity.

open as a page

A mapping layer reads each field by name at run time — what work does that add over a compiled field read?

level: middleimportance: must knowfreq 62%
basics
~20 s

Each read first resolves a name to a member, then passes an access check, then moves the value through a generic representation and an indirect call. A compiled read did the resolving once, at build time, and does none of the rest.

open as a page

A reflective write to a private field is refused before it runs — which boundary refused, and what must its owner grant?

level: middleimportance: must knowfreq 55%
basics
~20 s

Two layers guard a hidden member: the per-member access check, and, where a runtime groups types into packaged units, that unit's encapsulation boundary. A refusal of the suppression request itself points at the outer one, which only the unit's author or the deployer can open.

open as a page

A container picks a constructor at run time from the argument values it holds — how does that selection differ from the compiler's?

level: middleimportance: must knowfreq 55%
basics
~20 s

Run-time selection sees only the runtime classes of the actual values, never the declared types at a call site, so an absent value has no type at all, several constructors can match equally, and ambiguity surfaces at startup rather than in the build.

open as a page

A walker lists a loaded type's members two ways and gets two different sets — which two axes separate the lists?

level: middleimportance: must knowfreq 64%
basics
~20 s

Two axes: where a member is declared (on this type or inherited from a supertype) and how visible it is (any visibility or public only). Common lookups bundle the two, so each call answers a fixed combination rather than letting you choose.

open as a page

A marker attached to a class declaration changes nothing on its own — what must exist for it to have any effect?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Attached metadata is inert: something has to read it. A consumer — a build-time processor, a startup scanner, or a check at the call site — must look for that marker and act on what it says, or nothing happens.

open as a page

You declare a metadata type marking rate-limited operations. Why does its declaration name the kinds of declarations it may be attached to?

level: juniorimportance: must knowfreq 62%
basics
~20 s

A metadata type's declaration enumerates the declaration kinds it may be attached to - type, method, field, parameter - and attachments anywhere else are rejected. Target lists turn a misplaced marker into a build error instead of a silent no-op.

open as a page

A marker on a payroll method quietly makes the call transactional and retried, so what does that cost the reader of the call site?

level: middleimportance: must knowfreq 62%
basics
~10 s

Action-at-a-distance: the call site stops describing what the call does. Behaviour now depends on a marker plus machinery the reader cannot see, so local reasoning, debugging, review and onboarding all get more expensive.

open as a page

A marker can be consumed by a build-time processor, a startup scanner, or a call-site check — when does each catch a mistake?

level: middleimportance: must knowfreq 66%
basics
~20 s

A build-time processor catches a mistake while the build runs and can fail it. A startup scanner catches it when the process boots, after the artifact already shipped. A call-site check catches it only when that path actually executes.

open as a page

Your published rate-limit metadata type gains a burst member. How do you keep other teams' existing attachments compiling?

level: middleimportance: must knowfreq 50%
basics
~20 s

Declare the new member with a default value. A member without a default must be supplied at every attachment site, so adding one breaks existing uses; a defaulted member is optional and older attachments keep their meaning.

open as a page

Where should a hand-written addition to a generated telemetry type live, given the file is rewritten on every build?

level: middleimportance: must knowfreq 62%
basics
~20 s

In a hand-owned file the generator never writes, attached through an extension seam the generated type exposes: a type that extends it, a hook it calls, or a wrapper around it. Generated files are build output, not source.

open as a page

Generated source is emitted into the hand-edited tree beside its inputs — what does that habit cost a team?

level: middleimportance: must knowfreq 55%
basics
~20 s

Mixing machine-written files into the hand-edited tree invites edits that a later re-derivation erases, and it feeds emitted code to style checks, coverage numbers and review. Emit into a separate output directory and mark every file derived.

open as a page

How does a compiler-hosted processor tell the build which marked declarations it handles?

level: middleimportance: must knowfreq 58%
basics
~20 s

A processor advertises up front the set of metadata markers it supports. Each round the build hands it only the declarations carrying those markers, and the processor then applies its own filter for declaration kind and shape before emitting anything.

open as a page

Why is a processor's generated output fed through another processing round, and what makes the rounds stop?

level: middleimportance: must knowfreq 62%
basics
~20 s

Generated files are ordinary source: they can carry markers of their own, so the build re-runs processing over them. Each round's output becomes the next round's input, and the loop ends when a round emits no new file.

open as a page

A command-line tool binds configuration onto objects by generated code or run-time inspection: which work does each leave for startup?

level: middleimportance: must knowfreq 62%
basics
~20 s

Generation decides the binding plan during the build, so startup only runs straight-line assignments. Run-time inspection computes the same plan inside the running process, so member discovery, key matching and conversion choice all land in the startup window.

open as a page

When a macro body runs while the program is being compiled, which values can it splice into the code it emits?

level: middleimportance: must knowfreq 58%
basics
~20 s

Only values the expanding stage can render back as code: literals, names the emitted program can resolve, or fragments it built by quoting. A live compile-time object cannot be smuggled into run time; it has to be reconstructed.

open as a page

Why can a shorthand that substitutes argument text before parsing compute the wrong value inside a larger expression?

level: middleimportance: must knowfreq 62%
basics
~20 s

Substitution happens before the grammar is applied, so the pasted characters are parsed together with whatever surrounds the call. The caller's operators can bind tighter than the body's, regrouping the expression into something the author never wrote.

open as a page

A compile error points at a line nobody typed, inside a macro's output, so what makes that message actionable?

level: seniorimportance: must knowfreq 50%
basics
~20 s

Source positions carried on every emitted node, plus an expansion trace. Text derived from the caller's argument should point back at the call site, text the macro authored at the macro, and the trace should name the chain that produced it.

open as a page

An expansion introduces a temporary binding into the caller's block - how can that silently change the caller's meaning?

level: seniorimportance: must knowfreq 58%
basics
~20 s

The introduced binding shadows a caller name of the same spelling, so the caller's own references - including argument text spliced into the body - resolve to the expansion's temporary instead. This is accidental capture, and it compiles cleanly.

open as a page

When a compile-time expansion substitutes one argument expression at two places in its body, what does the caller lose?

level: middleimportance: should knowfreq 52%
basics
~20 s

Substitution copies the argument's expression, not its value, so the expression is evaluated once per occurrence that control flow reaches. Side effects and cost repeat, and two evaluations can disagree. Bind it once into a temporary instead.

open as a page

In a chained configuration API, what makes one call follow another, and what does the closing call add?

level: juniorimportance: must knowfreq 65%
basics
~20 s

Each configuring call returns a receiver carrying everything recorded so far, so the next call can be written straight onto it. The closing call is different: it validates the accumulated state and produces the finished result.

open as a page

In a nested policy notation built from blocks, what does it mean that each block runs against an implicit receiver?

level: juniorimportance: must knowfreq 58%
basics
~20 s

Each block is a piece of code the enclosing call evaluates against an object it supplies, so unqualified calls inside the block configure that object. Nesting blocks builds a tree: an inner block targets the node its enclosing call just created.

open as a page

When a host-language notation replaces a plain configuration API, what discoverability does a newcomer lose at the call site?

level: middleimportance: must knowfreq 55%
basics
~20 s

A plain API advertises itself: its members, signatures and defaults are all reachable from the call site. A notation replaces that enumerable list with vocabulary a newcomer must be told about, and has to publish it some other way.

open as a page

In a nested policy notation, why can a call inside an inner block silently configure the enclosing node instead?

level: middleimportance: must knowfreq 50%
basics
~20 s

Enclosing receivers stay in scope when an inner block opens, so a name the inner node cannot answer is looked up outward and answered by the enclosing node. The call is legal, nothing is misspelled, and the wrong node is configured.

open as a page

In a design review, which signals say a proposed domain notation will not repay its cost?

level: middleimportance: should knowfreq 47%
basics
~20 s

The notation does not repay when its authors are the people who wrote it, when its whole gain is punctuation an ordinary helper would remove, when its words are a one-for-one rename of existing members, or when it has begun growing its own control flow.

open as a page

A caching wrapper speeds up calls from outside an object but never fires for a call the object makes on itself, why?

level: middleimportance: must knowfreq 66%
basics
~20 s

Interception only runs where a call crosses from one object into another. A wrapper sees the calls made through the reference it was handed; a call the object makes on itself travels through its own self-reference and never reaches the wrapper.

open as a page

Which calls does a stand-in that intercepts by overriding members silently miss, even when every caller goes through it?

level: middleimportance: must knowfreq 50%
basics
~20 s

Anything that is not a dynamically dispatched call on the instance: members no subtype may replace, members resolved from the declared type or with no receiver at all, direct field reads, and work the target did while it was being constructed.

open as a page

When a request passes through a chain of interceptors around one call, in what order do their after-the-call parts run?

level: middleimportance: must knowfreq 62%
basics
~20 s

In reverse of the order they were entered. Handlers nest rather than queue: each one calls inward and waits, so the handler entered last is the first to resume, and the outermost handler finishes last.

open as a page

Which run-time stand-in works when the caller holds a concrete class rather than a declared contract, and what must that class allow?

level: middleimportance: must knowfreq 55%
basics
~20 s

Only a stand-in generated as a subtype of that class can be substituted there; in a nominally typed language a contract-only stand-in is not of that type. The subtype needs an extensible class, overridable members and construction that is safe to repeat.

open as a page

A chat client's remote-service handle is synthesized at run time from a declared contract: what does each generated member body do?

level: middleimportance: must knowfreq 60%
basics
~20 s

Every generated member body does the same thing: it packages a description of the member that was called together with the argument list, hands both to one handler object, and returns whatever that handler returns.

open as a page