Elaborator framework for (mutual) inductives #
This module defines the framework for elaborating (mutually recursive) inductive types.
Commands such as inductive and structure plug into this framework,
and the framework orchestrates their mutual elaboration.
The main entry point for new inductive commands are the @[builtin_inductive_elab]/@[inductive_elab] attributes,
which register a handler for a given decl syntax.
The main entry point to elaboration are the functions
Lean.Elab.Command.isInductiveCommandto determine whether syntax has a registered inductive elaborator.Lean.Elab.Command.elabInductivesto elaborate a list of syntaxes as mutually inductive types.Lean.Elab.Command.elabInductiveto elaborate a single inductive command.
For mutual ... end in particular:
Lean.Elab.Command.isMutualInductiveto check whether each syntax element of amutual ... endblock has a registered inductive elaborator.Lean.Elab.Command.elabMutualInductiveto elaborate a list of syntaxes satisfyingisMutualInductiveas a mutually inductive block.
See the docstring on Lean.Elab.Command.InductiveElabDescr for an overview of the framework.
View of a constructor. Only ref, modifiers, declName, and declId are required by the mutual inductive elaborator itself.
- ref : Syntax
Syntax for the whole constructor.
- modifiers : Modifiers
- declName : Name
Fully qualified name of the constructor.
- declId : Syntax
Syntax for the name of the constructor, used to apply terminfo. If the source is synthetic, terminfo is not applied.
- binders : Syntax
For handler use. The
inductiveuses it for the binders to the constructor. For handler use. The
inductivecommand uses it for the resulting type for the constructor.
Instances For
Equations
A view for generic inductive types.
- ref : Syntax
- declId : Syntax
- modifiers : Modifiers
- isClass : Bool
- allowIndices : Bool
Whether the command should allow indices (like
inductive) or not (likestructure). - allowSortPolymorphism : Bool
Whether the command supports creating inductive types that can be polymorphic across both
PropandType _. If false, then either the universe must bePropor it must be of the formType _. - shortDeclName : Name
- declName : Name
- binders : Syntax
- computedFields : Array ComputedFieldView
- derivingClasses : Array DerivingClassView
The declaration docstring, and whether it's Verso
- isCoinductive : Bool
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Elaborated header for an inductive type before fvars for each inductive are added to the local context.
- view : InductiveView
- numParams : Nat
- type : Expr
The parameters in the header's initial local context. Used for adding fvar alias terminfo.
Instances For
Equations
Instances For
Equations
Instances For
An intermediate step for mutual inductive elaboration. See InductiveElabDescr
Finalize the inductive type, after they are all added to the environment, after auxiliary definitions are added, and after computed fields are registered. The
levelParams,params, andreplaceIndFVarsarguments ofprefinalizeare still valid here.
Instances For
An intermediate step for mutual inductive elaboration. See InductiveElabDescr.
- ctors : List Constructor
The constructors produced by
InductiveElabStep1. - collectUsedFVars : StateRefT' IO.RealWorld CollectFVars.State MetaM Unit
Function to collect additional fvars that might be missed by the constructors. It is permissible to include fvars that do not exist in the local context (
structurefor example includes its field fvars). - collectExtraHeaderLMVars : StateRefT' IO.RealWorld CollectLevelMVars.State MetaM Unit
Function to collect additional level metavariables that are header-like (e.g. in
extendsclauses instructure). Header-like level metavariables can be promoted to be level parameters. Function to check universes and provide a custom error. (
structureuses this to do checks per field to give nicer messages.)Step to do final term elaboration error reporting, done immediately after universe levels are fully elaborated, but before the final level checks.
- prefinalize (levelParams : List Name) (params : Array Expr) (replaceIndFVars : Expr → MetaM Expr) : TermElabM InductiveElabStep3
Like
finalize, but occurs beforeafterTypeCheckingattributes.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
An intermediate step for mutual inductive elaboration. See InductiveElabDescr.
- view : InductiveView
- elabCtors (rs : Array ElabHeaderResult) (r : ElabHeaderResult) (params : Array Expr) : TermElabM InductiveElabStep2
Instances For
Equations
Instances For
Descriptor for a command processor that elaborates an inductive type.
Elaboration occurs in the following steps:
- Every command has its
mkInductiveViewevaluated, producingInductiveViews and callbacks for the next steps (all recorded inInductiveElabStep1). - Each
InductiveViewgets elaborated, creatingElabHeaderResults, and the local contexts are unified into a single one with a consistent set of parameters between each inductive. - Each callback is called to elaborate each inductives' constructors and some additional callbacks
(all recorded in
InductiveElabStep2). - Fvars are collected from the constructors and from the
InductiveStep2.collectUsedFVarscallbacks. This is used to compute the final set of scoped variables that should be used as additional parameters. - Universe levels are checked. Commands can give custom errors using
InductiveStep2.checkUniverses. - Elaboration of constructors is finalized, with additional tasks done by each
InductiveStep2.finalizeTermElab. - The inductive family is added to the environment and is checked by the kernel.
- Attributes and other finalization activities are performed, including those defined
by
InductiveStep2.prefinalizeandInductiveStep3.finalize.
- mkInductiveView : Modifiers → Syntax → TermElabM InductiveElabStep1
Instances For
Equations
- Lean.Elab.Command.instInhabitedInductiveElabDescr.default = { mkInductiveView := default }
Instances For
Registers an inductive type elaborator for the given syntax node kind.
Commands registered using this attribute are allowed to be used together in mutual blocks with
other inductive type commands. This attribute is mostly used internally for inductive and
structure.
An inductive type elaborator should have type Lean.Elab.Command.InductiveElabDescr.
Returns true if the syntax participates in the mutual inductive elaborator.
These do not need to be commands. In fact inductive and structure are registered
on the Lean.Parser.Command.inductive and Lean.Parser.Command.structure syntaxes.
Equations
- Lean.Elab.Command.isInductiveCommand stx = do let env ← Lean.getEnv pure !(Lean.Elab.Command.inductiveElabAttr.getEntries env stx.getKind).isEmpty
Instances For
Initializes the elaborator associated to the given syntax.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Executes k using the Syntax reference associated with constructor ctorName.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Runs k with the resulting type as the ref or, if that's not available, with the view's ref.
Equations
- view.withTypeRef k = Lean.withRef view.ref (Lean.withRef? view.type? k)
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Index-to-parameter promotion #
The basic convention for inductive is that the binders before the colon in a type former
correspond to the type parameters, and the pi types after the colon correspond to the indices.
Indices that are fixed across all occurrences of the type former can potentially be promoted to be parameters instead. Motivations:
- The recursor is more efficient (though the induction principle might be more rigid than expected; this is solved by generalizing variables)
- The inferred universe level of the inductive type might decrease.
Recall that when an inductive type declaration Declaration.inductDecl is sent to the kernel,
every constructor is provided its full type, including the inductive parameters.
The inductDecl declaration itself specifies how many parameters there are (nparams),
and the first nparams parameters of a constructor are the parameters for the inductive type.
The function fixedIndicesToParams determines by how much nparams can be increased
without resulting in an invalid inductive type declaration.
We say the indices that become parameters this way are promoted.
Only a prefix of indices can be promoted because of this.
Note that it currently does not do any constructor parameter reordering.
If the parameter for an index does not appear in the correct position in a constructor for it to be
an inductive type parameter, it is not promoted.
That said, the inductive command itself has some capacity for reordering
in Lean.Elab.Command.reorderCtorArgs.
Promotion can be disabled with set_option inductive.autoPromoteIndices false.
Level metavariable inference #
Terminology: Given the inductive type former I : ... -> Sort u,
we call Sort u the resulting universe of I and u the resulting universe level of I.
After constructor elaboration, inductive type declarations still often have universe level metavariables. They may appear anywhere:
- in types of inductive parameters/indices
- in the resulting universe level of the type former
- and in constructor fields (i.e. those parameters after inductive parameters).
The resulting universe has a strong bearing on what sort of inference we can do:
- If it is
Prop, the type is an inductive predicate, and it is impredicative, meaning fields can come from any universe. - Otherwise, fields are subject to the universe level inequality (described further below).
There are certain cases where we choose to infer Prop for the resulting universe.
Recall that an inductive type is a syntactic subsingleton if any two terms of that type
are definitionally equal. Syntactic subsingletons can eliminate into Type, hence can be pattern matched.
This justifies setting the resulting universe Prop if a type is "obviously" meant to be an inductive predicate.
Roughly, we say an inductive type is "obviously" meant to be an inductive predicate if the type
is not recursive, has exactly one constructor, the constructor has at least one field, and every field is Prop.
See isPropCandidate for further explanation.
From now on, we will talk only about inductive types in Type or above.
There are a number of considerations for inference:
The universe level inequality. If
uis the resulting universe level, then for every fieldf : ftyof every constructor, we must havev ≤ u, wherefty : Sort v.Avoiding
Sortpolymorphism. We say a type isSort-polymorphic if, depending on the values of level parameters, the resulting universe can be bothPropandType _. These usually cannot eliminate intoPropunless they are syntactic subsingletons, so we wish to avoidSortpolymorphism to avoid users' unpleasant surprises. We do this by adding the constraint1 ≤ u.Using the minimal resulting universe if it exists. If
uhas metavariables, then we do not wantuto be unnecessarily large. We want to find the minimal levels to assign to the metavariables subject to the above constraints. However: we only entertain unique solutions. We report ambiguous situations to the user. Furthermore, this is best-effort. Users can always provide an explicit resulting universe. (We can also detect certain situations where this procedure computes a universe that is likely larger than what could be if it had been explicitly provided before constructor elaboration.)Avoiding inferring proof fields. If
vis the universe level for a constructor field, then we avoid assigning metavariable invto anything that could potentially givePropfields orSortpolymorphism. To do this, we use the weak constraint1 ≤w v. Definition:1 ≤w pand1 ≤w kfor all parameterspand constantsk.1 ≤w succ vfor all levelsv1 ≤w max a band1 ≤w imax a biff1 ≤w aand1 ≤w b1 ≤w ?viff1 ≤ ?v. The effect is that1 ≤w vresolves to a set of1 ≤ ?vmetavariable constraints. This is more conservative than it needs to be, but it is uniform and avoids SAT-like reasoning; effectively, inference analyzes each metavariable, independently from one another.
Using the unique universe for fields if it exists, preferring non-constant solutions. Uniqueness is with respect to the set of universe level expressions, not universe levels. Examples:
?a ≤ 0~~~>?a = 0is the unique solution. (Note: this can lead to unpleasant surprises, where a field unexpectedly becomes a proof. We currently do nothing about this. It would require more careful global analysis.)?a ≤ 1~~~> either?a = 0or?a = 1, so underconstrained unless we have1 ≤ ?a, in which case?a = 1is the unique solution.?a ≤ vwithva level parameter ~~~> syntactically have?a = 0and?a = vas solutions, but prefer?a = vsince it is non-constant, and is the unique solution.?a ≤ v + 1withva level parameter ~~~> syntactically have?a = 0,?a = 1,?a = v, and?a = v + 1as solutions, with?a = vand?a = v + 1preferred. This is underconstrained, unless we have1 ≤ ?a, in which case?a = v + 1is the unique solution.?a ≤ max v₁ v₂withv₁andv₂level parameters ~~~> syntactically have?a = 0,?a = v₁,?a = v₂, and?a = max v₁ v₂as solutions, with the latter three preferred. This is underconstrained.
The heuristic for preferring non-constant solutions comes from user examples.
Collecting the constraints, these rules imply the only assignments considered are:
- if
0 ≤ ?a ≤ kandk = 0then assign?a := 0 - if
0 ≤ ?a ≤ p + kandk = 0then assign?a := p - if
1 ≤ ?a ≤ kandk = 1then assign?a := 1 - if
1 ≤ ?a ≤ p + kandk = 1then assign?a := p + 1
Otherwise we do not assign ?a, and we leave it to the "declaration has metavariables" error to report it.
There are also other considerations to make the inference more effective.
If the resulting universe is of the form max ?m 1, we want to isolate ?m as
the metavariable to solve for.
The simplifyResultingUniverse u procedure is a heavy hammer to reliably extract
the "generic part" of the resulting universe level that is suitable for the inference procedures,
without the fragility of matching on specific patterns.
It gives a universe level u' ≤ u such that u' and u are equivalent "at infinity",
which means u' = u for all sufficiently high assignments of the parameters or metavariables.
In general, we only attempt solving for any universe levels at each stage
if u' is of the form r + k where r is a parameter, metavariable, or zero,
since in other cases solutions are not unique.
High-level inference algorithm:
- Solve for the metavariable in the resulting universe level.
- Solve for metavariables in constructor fields, if possible.
- Turn level metavariables in the type former (and from
collectExtraHeaderLMVars) into level parameters. - Report unsolved level metavariables.
Examples are in tests/lean/run/inductive_univ.lean.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- Lean.Elab.Command.elabInductive modifiers stx = Lean.Elab.Command.elabInductives #[(modifiers, stx)]
Instances For
Returns true if all elements of the mutual block (Lean.Parser.Command.mutual) are inductive declarations.
Equations
- Lean.Elab.Command.isMutualInductive stx = Array.allM (fun (elem : Lean.Syntax) => Lean.Elab.Command.isInductiveCommand elem[1]) stx[1].getArgs
Instances For
Elaborates a mutual block, assuming the commands satisfy Lean.Elab.Command.isMutualInductive.
Equations
- One or more equations did not get rendered due to their size.