Expand description
R4 — the elaborator: metavariables, unification, and implicit-argument inference.
The layer between the surface language and the trusted kernel. A user writes id 0
and means id Nat 0; length xs and means length Nat xs. The elaborator fills the
gaps: it inserts METAVARIABLES (?A, …) for the omitted/implicit arguments and SOLVES
them by UNIFICATION against the types it infers, producing a fully-explicit kernel
Term that infer_type then re-checks. Nothing here is trusted — elaboration only
constructs a term; the kernel still certifies it.
A metavariable is represented as a Term::Var whose name starts with ? (a character
no real binder uses), so the whole machinery rides on the existing Term with no new
variant. Scope: first-order unification with the occurs-check, and implicit-argument
insertion driven by an explicit implicit/explicit mask — the core that makes the
surface usable. (Higher-order / pattern unification is the next layer.)
Structs§
- MetaCtx
- The metavariable context: fresh-name supply + the substitution being solved.
Enums§
- Param
Kind - How a function parameter is supplied during elaboration.
Constants§
- ANON_
CTOR_ MARKER - Reserved head marking a surface anonymous constructor
⟨e₁, …, eₙ⟩(E3). The parser emits⟨anon⟩ e₁ … eₙ; the elaborator (this module) rewrites it to the sole constructor of the expected inductive and then discards the marker — it NEVER reaches a kernel term. The name contains⟨/⟩, characters the identifier lexer cannot produce, so it can never collide with a user global. Like the?-prefixed metavariables above, this rides on the existingTermwith no new variant. - DOT_
MARKER - Reserved head marking surface dot notation
receiver.field(E4). The parser emits⟨proj⟩ receiver field(the field name carried as aGlobal); the elaborator rewrites it to the projectionH_field params… receiverand discards the marker. Collision-proof for the same reason asANON_CTOR_MARKER.
Functions§
- auto_
bind_ implicits - Auto-bind free type variables as leading implicit parameters. A definition written
id : A -> A := fun a : A => amentionsAas a FREE, unregistered, single-uppercase global — the type-variable convention. This pass generalizes each such variable: it prependsΠ(A:Type)to the type andλ(A:Type)to the body (convertingGlobal(A)to the boundVar(A)), and returns the new implicit count, soAbecomes an inferred implicit argument. Definitions that already bind their parameters reference them asVars, not freeGlobals, so they are untouched — this only rescues what was previously an unbound-variable error. - bind_
self_ recursion - Replace every
Global(name)byVar(name)(turning a free type-variable reference into a reference to the binder this pass prepends). Recursive-definition sugar: ifbodyrefers to the definition’s ownnameas a freeGlobal, bind that self-reference with afixso the definition can call itself —Definition f : T := … f … .becomesf := fix f. …. The kernel’s termination guard (run byinfer_type) then certifies the recursion decreases structurally; a body with no self-reference (or one that already wrote an explicitfix, whose self-references are already boundVars) is returned unchanged. A self-reference SHADOWS any same-named global, so a recursiveDefinition addoverNatoverrides a built-inadd. - elaborate
- Elaborate
term(which may containHoles) against an optionalexpectedtype, solving the holes by unification. Returns the elaborated term and its type (both still containing metavariables untilinstantiated). - elaborate_
anon_ ctor - Elaborate an ANONYMOUS CONSTRUCTOR
⟨f₀, …, fₙ⟩(E3) against an EXPECTED inductive/ structure typeH a…. It appliesH’s (unique) constructor to the type parametersa…, read off the expected type, then the field values — so⟨Zero, true⟩expected atProd Nat BoolbecomesProd_mk Nat Bool Zero true. Each field is elaborated against its declared type (so coercions/implicits fire), and the whole is kernel-certified. - elaborate_
app - Elaborate an application of
head(of typehead_ty) where leading parameters may be implicit or instance-implicit (kinds[i]). A fresh metavariable is inserted for each implicit position; instance positions get a placeholder metavariable whose resolution is DEFERRED until all explicit arguments (which may pin the class’s type variables) have been unified. Returns the fully explicit, metavariable-instantiated term and type. - elaborate_
app_ against - Like
elaborate_appbut with an EXPECTED result type. After the explicit arguments are processed, the result type is unified withexpected— so an implicit that the arguments alone do not pin (e.g. theAofnil : {A} → List A, which has no value argument) is solved from the surrounding context. The expected-type pass runs BEFORE instance resolution, so a class’s type variable can be fixed by the context too. - elaborate_
dot - Elaborate DOT notation
receiver.field(E4). The receiver’s type head names an inductive/structureH; the projection isH_field(K4’s convention), applied toH’s parameters — read off the receiver’s type — and then the receiver itself. Sop.fstwithp : Prod A BbecomesProd_fst A B p. Returns an error if no such projection exists or the result does not type-check. - elaborate_
in - Like
elaboratebut under a LOCAL CONTEXTlctxof bound variables.ctxis the kernel context already EXTENDED with those same variables (soinfer_typesees them at the leaves), whilelctxis the parallel(name, type)list the unifier uses for higher-order PATTERN unification. Descending under aλextends both — which is how a metavariable applied to a bound variable (a motive?P n) gets solved in a body. - fill_
match_ motives - Fill in inferred motives for
matchexpressions written WITHOUT areturnclause (theHolemotive the parser leaves). The motive is a CONSTANTλ_:I. T— covering non-dependent matches — whereTis the EXPECTED type (a definition’s declared result type, propagated through binders) or, lacking one, the type of a nullary first branch. The pass threads the kernel context through binders so the discriminant’s type resolves; amatchit cannot give a motive (a dependent case, no expected type) is reported so the user can add an explicitreturn. - instantiate
- Substitute every solved metavariable throughout
term(transitively, since a solution may itself mention other metavariables — the occurs-check keeps this terminating). - is_meta
- Whether
namedenotes a metavariable (the?-prefix convention). - resolve_
coercion - Find a registered coercion carrying
fromtoto(up to unification), returning the coercion FUNCTION to wrap the argument in — Lean’s↑. The elaborator calls this when an argument’s type does not match the expected parameter type; a match commits the unification (a polymorphic coercion’s type variables get solved). - resolve_
instance - Search the
Context’s instance database for an instance provingrequired, returning the (metavariable-instantiated) instance term. Handles POLYMORPHIC / RECURSIVE instances (instance {A} [Inhabited A] : Inhabited (List A)): the instance’s parameter telescope is freshened to metavariables, its conclusion is unified againstrequired(solving the type parameters), and each PREMISE parameter is then resolved RECURSIVELY. The first instance that fully resolves wins; failed trials never pollutemctx(each runs on a clone, committed only on success). - surface_
elaborate - Elaborate a whole surface term: walk it, and at every application of a global with
declared implicit parameters (
Context::implicit_args), insert and infer those arguments — soid 0becomesid Int 0. Terms with no implicits are returned unchanged. The result is fully explicit and metavariable-free; the kernel certifies it as usual. This is the seam that wires the elaborator into the REPL. - surface_
elaborate_ against - Like
surface_elaboratebut with an EXPECTED type (e.g. a definition’s declared type). The expected type is propagated to the top-level application/global so an implicit with no value argument —nil : {A} → List Aused where aList Intis wanted — is inferred from context. - unify
- First-order unification of
aandb, solving metavariables intomctx. Returns whether they were made equal. A metavariable unifies with anything (after the occurs-check); everything else decomposes structurally. - unify_
in - Unify
aandbunder a LOCAL CONTEXTlctx((name, type)of the bound variables in scope). The local context enables higher-order PATTERN (Miller) unification:?M x̄ =?= t, where?Mis a metavariable applied to distinct bound variablesx̄, is solved by?M := λx̄. t. The top-levelunifyruns with an empty context, so its behavior — and every existing caller — is the first-order one.