<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://invarians.org/index.php?action=history&amp;feed=atom&amp;title=Lean_theorem_prover%2FSystem%2FElaboration</id>
	<title>Lean theorem prover/System/Elaboration - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://invarians.org/index.php?action=history&amp;feed=atom&amp;title=Lean_theorem_prover%2FSystem%2FElaboration"/>
	<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Elaboration&amp;action=history"/>
	<updated>2026-10-21T08:17:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://invarians.org/index.php?title=Lean_theorem_prover/System/Elaboration&amp;diff=6786&amp;oldid=prev</id>
		<title>PMA Publisher: Published from the Principia Machina source</title>
		<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Elaboration&amp;diff=6786&amp;oldid=prev"/>
		<updated>2026-10-15T19:35:04Z</updated>

		<summary type="html">&lt;p&gt;Published from the Principia Machina source&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw-interface=&quot;&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 19:35, 15 October 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l1&quot;&gt;Line 1:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 1:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{DISPLAYTITLE:Elaboration}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{DISPLAYTITLE:Elaboration}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Plebeian|Lean theorem prover/Explainers/What a proof assistant does|Lean theorem prover/Explainers/When Lean says no}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Plebeian|Lean theorem prover/Explainers/What a proof assistant does|Lean theorem prover/Explainers/When Lean says no}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Lean theorem prover map}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;Elaboration&amp;#039;&amp;#039;&amp;#039; is the step that turns what a person types into a term of Lean&amp;#039;s core type theory. The typed text leaves out a great deal: the types of arguments, the instances behind notation such as &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt;, conversions between number types, universe levels. The &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; supplies all of it and produces a fully explicit term. That term then goes to the Lean kernel, which checks it. The elaborator&amp;#039;s own reasoning is not trusted. This page follows a term from text to the Lean kernel and shows, with Lean 4.34.1&amp;#039;s own output, what the elaborator adds.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;Elaboration&amp;#039;&amp;#039;&amp;#039; is the step that turns what a person types into a term of Lean&amp;#039;s core type theory. The typed text leaves out a great deal: the types of arguments, the instances behind notation such as &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt;, conversions between number types, universe levels. The &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; supplies all of it and produces a fully explicit term. That term then goes to the Lean kernel, which checks it. The elaborator&amp;#039;s own reasoning is not trusted. This page follows a term from text to the Lean kernel and shows, with Lean 4.34.1&amp;#039;s own output, what the elaborator adds.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>PMA Publisher</name></author>
	</entry>
	<entry>
		<id>https://invarians.org/index.php?title=Lean_theorem_prover/System/Elaboration&amp;diff=6579&amp;oldid=prev</id>
		<title>PMA Publisher: Published from the Principia Machina source</title>
		<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Elaboration&amp;diff=6579&amp;oldid=prev"/>
		<updated>2026-10-14T18:01:04Z</updated>

		<summary type="html">&lt;p&gt;Published from the Principia Machina source&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{DISPLAYTITLE:Elaboration}}&lt;br /&gt;
{{Plebeian|Lean theorem prover/Explainers/What a proof assistant does|Lean theorem prover/Explainers/When Lean says no}}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Elaboration&amp;#039;&amp;#039;&amp;#039; is the step that turns what a person types into a term of Lean&amp;#039;s core type theory. The typed text leaves out a great deal: the types of arguments, the instances behind notation such as &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt;, conversions between number types, universe levels. The &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; supplies all of it and produces a fully explicit term. That term then goes to the Lean kernel, which checks it. The elaborator&amp;#039;s own reasoning is not trusted. This page follows a term from text to the Lean kernel and shows, with Lean 4.34.1&amp;#039;s own output, what the elaborator adds.&lt;br /&gt;
&lt;br /&gt;
== From text to the Lean kernel ==&lt;br /&gt;
&lt;br /&gt;
Lean processes a source file one command at a time. Each command is parsed, then elaborated, and a new definition or theorem is checked by the Lean kernel and recorded in Lean&amp;#039;s environment, its table of definitions and theorems. Each command can change how later commands are read, since a command may add new syntax.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot;&amp;gt;Lean Developers, &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039;, version 4.34.0, chapter &amp;quot;Elaboration and Compilation&amp;quot;. https://lean-lang.org/doc/reference/4.34.0/Elaboration-and-Compilation/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;parser&amp;#039;&amp;#039;&amp;#039; turns characters into a syntax tree of type &amp;lt;code&amp;gt;Syntax&amp;lt;/code&amp;gt;. The reference manual describes it as a recursive-descent parser that uses dynamic tables based on Pratt parsing, and its tables can be extended by any command.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; A syntax tree records what was written, not what it means: &amp;lt;code&amp;gt;x + 1&amp;lt;/code&amp;gt; parses the same whether &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is a natural number, an integer or a matrix.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; gives the tree its meaning. Term elaborators, in the manual&amp;#039;s words, map syntax and an optional expected type to a core language expression, and they work in a setting that provides unification, type class instance synthesis and type checking.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; The result is an &amp;lt;code&amp;gt;Expr&amp;lt;/code&amp;gt;, the same data type the Lean kernel checks.&lt;br /&gt;
&lt;br /&gt;
== Macro expansion ==&lt;br /&gt;
&lt;br /&gt;
Much of Lean&amp;#039;s notation is defined by &amp;#039;&amp;#039;&amp;#039;macros&amp;#039;&amp;#039;&amp;#039;: rules that rewrite one piece of syntax into another before it is given meaning. Macro expansion is not a separate pass. It is interleaved with elaboration.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; In Lean 4.34.1 the main elaboration loop first asks whether a macro applies to the syntax at hand; if one does, it elaborates the expansion instead, and only otherwise does it call an elaboration function registered for that kind of syntax.&amp;lt;ref name=&amp;quot;src-termelab&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Lean/Elab/Term/TermElabM.lean&amp;lt;/code&amp;gt;: the function &amp;lt;code&amp;gt;elabTermAux&amp;lt;/code&amp;gt; and the type &amp;lt;code&amp;gt;SyntheticMVarKind&amp;lt;/code&amp;gt;. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/Elab/Term/TermElabM.lean&amp;lt;/ref&amp;gt; The trace option &amp;lt;code&amp;gt;trace.Elab.step&amp;lt;/code&amp;gt; prints each step together with the expected type at that point:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean&amp;quot; style=&amp;quot;background: #f8f9fa; border: 1px solid #c8ccd1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
set_option trace.Elab.step true in&lt;br /&gt;
#check (3 : Nat) ≠ 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean-output&amp;quot; style=&amp;quot;background: #ffffff; border: 1px dashed #a2a9b1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
3 ≠ 4 : Prop&lt;br /&gt;
[Elab.step] ✅️ expected type: &amp;amp;lt;not-available&amp;amp;gt;, term&lt;br /&gt;
    (3 : Nat) ≠ 4&lt;br /&gt;
  [Elab.step] ✅️ expected type: &amp;amp;lt;not-available&amp;amp;gt;, term&lt;br /&gt;
      binrel% Ne✝ (3 : Nat) 4&lt;br /&gt;
    [Elab.step] ✅️ expected type: &amp;amp;lt;not-available&amp;amp;gt;, term&lt;br /&gt;
        (3 : Nat)&lt;br /&gt;
      [Elab.step] ✅️ expected type: Type, term&lt;br /&gt;
          Nat&lt;br /&gt;
        [Elab.step.result] Nat&lt;br /&gt;
      [Elab.step] ✅️ expected type: Nat, term&lt;br /&gt;
          3&lt;br /&gt;
        [Elab.step.result] 3&lt;br /&gt;
      [Elab.step.result] 3&lt;br /&gt;
    [Elab.step] ✅️ expected type: &amp;amp;lt;not-available&amp;amp;gt;, term&lt;br /&gt;
        4&lt;br /&gt;
      [Elab.step.result] 4&lt;br /&gt;
    [Elab.step.result] 3 ≠ 4&lt;br /&gt;
  [Elab.step.result] 3 ≠ 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The symbol &amp;lt;code&amp;gt;≠&amp;lt;/code&amp;gt; is a macro. Its expansion, &amp;lt;code&amp;gt;binrel% Ne✝ (3 : Nat) 4&amp;lt;/code&amp;gt;, hands the two sides to &amp;lt;code&amp;gt;binrel%&amp;lt;/code&amp;gt;, an elaborator for binary relations. The dagger after &amp;lt;code&amp;gt;Ne&amp;lt;/code&amp;gt; marks a name introduced by the macro, which text written by the user cannot capture; [[Lean theorem prover/System/Syntax extension and hygienic macros|syntax extension and hygienic macros]] explains this hygiene, which the paper introducing Lean 4 describes as capture-avoiding.&amp;lt;ref name=&amp;quot;lean4-paper&amp;quot; /&amp;gt; The ascription &amp;lt;code&amp;gt;(3 : Nat)&amp;lt;/code&amp;gt; passes the expected type &amp;lt;code&amp;gt;Nat&amp;lt;/code&amp;gt; down to the numeral &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Expected types ==&lt;br /&gt;
&lt;br /&gt;
The expected type is the elaborator&amp;#039;s main source of information. Some notation cannot be read at all without it. The anonymous constructor &amp;lt;code&amp;gt;⟨1, 2⟩&amp;lt;/code&amp;gt; names no type, so on its own it fails:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-fails&amp;quot; style=&amp;quot;background: #f8f9fa; border: 1px solid #c8ccd1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
#check ⟨1, 2⟩&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lean answers &amp;lt;code&amp;gt;Invalid `⟨...⟩` notation: The expected type of this term could not be determined&amp;lt;/code&amp;gt;. With an ascription, &amp;lt;code&amp;gt;#check (⟨1, 2⟩ : Nat × Nat)&amp;lt;/code&amp;gt; prints &amp;lt;code&amp;gt;(1, 2) : Nat × Nat&amp;lt;/code&amp;gt;: knowing the type, the elaborator finds the structure&amp;#039;s one constructor and applies it.&lt;br /&gt;
&lt;br /&gt;
== What the elaborator fills in ==&lt;br /&gt;
&lt;br /&gt;
The option &amp;lt;code&amp;gt;pp.all&amp;lt;/code&amp;gt; prints a term with nothing hidden: every implicit argument, every instance, every universe level. It is the way to see what the elaborator actually produced.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean&amp;quot; style=&amp;quot;background: #f8f9fa; border: 1px solid #c8ccd1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
#check @id&lt;br /&gt;
set_option pp.all true in&lt;br /&gt;
#check id 5&lt;br /&gt;
set_option pp.all true in&lt;br /&gt;
#check (2 : Int) + (3 : Nat)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean-output&amp;quot; style=&amp;quot;background: #ffffff; border: 1px dashed #a2a9b1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
@id : {α : Sort u_1} → α → α&lt;br /&gt;
@id.{1} Nat (@OfNat.ofNat.{0} Nat (nat_lit 5) (instOfNatNat (nat_lit 5))) : Nat&lt;br /&gt;
@HAdd.hAdd.{0, 0, 0} Int Int Int (@instHAdd.{0} Int Int.instAdd)&lt;br /&gt;
  (@OfNat.ofNat.{0} Int (nat_lit 2) (@instOfNat (nat_lit 2)))&lt;br /&gt;
  (@Nat.cast.{0} Int instNatCastInt (@OfNat.ofNat.{0} Nat (nat_lit 3) (instOfNatNat (nat_lit 3)))) : Int&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Four kinds of filling are visible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Implicit arguments.&amp;#039;&amp;#039;&amp;#039; The braces in &amp;lt;code&amp;gt;{α : Sort u_1}&amp;lt;/code&amp;gt; mark an argument the user does not write. For &amp;lt;code&amp;gt;id 5&amp;lt;/code&amp;gt; the elaborator creates a placeholder for &amp;lt;code&amp;gt;α&amp;lt;/code&amp;gt;, a &amp;#039;&amp;#039;metavariable&amp;#039;&amp;#039;, and unifies it with the type of the argument &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;, which is itself still open; it becomes &amp;lt;code&amp;gt;Nat&amp;lt;/code&amp;gt; when the default instance for numerals is applied, as described under postponement below. [[Lean theorem prover/System/Metavariables and unification|Metavariables and unification]] treats this machinery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Universe levels.&amp;#039;&amp;#039;&amp;#039; The universe parameter &amp;lt;code&amp;gt;u_1&amp;lt;/code&amp;gt; of &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; is instantiated to &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, since &amp;lt;code&amp;gt;Nat : Type&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;Sort 1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Instances.&amp;#039;&amp;#039;&amp;#039; A numeral is not a primitive: &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;OfNat.ofNat Nat 5 (instOfNatNat 5)&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; becomes the method &amp;lt;code&amp;gt;HAdd.hAdd&amp;lt;/code&amp;gt; of a type class with the instance &amp;lt;code&amp;gt;instHAdd&amp;lt;/code&amp;gt; built on &amp;lt;code&amp;gt;Int.instAdd&amp;lt;/code&amp;gt;. The instances are found by search; [[Lean theorem prover/System/Type classes and instance resolution|type classes and instance resolution]] describes it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Coercions.&amp;#039;&amp;#039;&amp;#039; The second summand was written as a natural number but is used where an integer is expected. The elaborator inserts &amp;lt;code&amp;gt;Nat.cast&amp;lt;/code&amp;gt;, which the ordinary printer shows as &amp;lt;code&amp;gt;↑&amp;lt;/code&amp;gt;. The reference manual states that a coercion is used when a term of one type appears where another is expected, and that coercions are found by type class instance synthesis.&amp;lt;ref name=&amp;quot;manual-coe&amp;quot;&amp;gt;Lean Developers, &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039;, version 4.34.0, chapter &amp;quot;Coercions&amp;quot;. https://lean-lang.org/doc/reference/4.34.0/Coercions/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Postponement ==&lt;br /&gt;
&lt;br /&gt;
The elaborator works through a term roughly from left to right, but information often arrives late. A subterm may need a type that a later argument determines, or an instance problem may still contain unknowns. In such cases the elaborator does not fail. It puts a &amp;#039;&amp;#039;&amp;#039;synthetic metavariable&amp;#039;&amp;#039;&amp;#039; in place of the unfinished work and comes back to it. In Lean 4.34.1 such a placeholder has one of four kinds: an instance to be synthesised, a coercion to be inserted, a tactic block to be run, or a term whose elaboration was postponed.&amp;lt;ref name=&amp;quot;src-termelab&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The pending placeholders are resumed in a fixed order. Lean retries them while progress is being made. When none progresses, it retries without allowing further postponement, then applies &amp;#039;&amp;#039;&amp;#039;default instances&amp;#039;&amp;#039;&amp;#039; to instance problems still unsolved, then forces the remaining problems to succeed or report an error. Tactic blocks run last, when the rest of the term is as complete as it will get.&amp;lt;ref name=&amp;quot;src-synth&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Lean/Elab/SyntheticMVars.lean&amp;lt;/code&amp;gt;: the function &amp;lt;code&amp;gt;synthesizeSyntheticMVars&amp;lt;/code&amp;gt;. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/Elab/SyntheticMVars.lean&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Default instances explain a small puzzle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean&amp;quot; style=&amp;quot;background: #f8f9fa; border: 1px solid #c8ccd1; padding: 0.6em; margin: 0.3em 0; font-size: 0.85em; line-height: 1.35; overflow-x: auto; white-space: pre;&amp;quot;&amp;gt;&lt;br /&gt;
#check fun x =&amp;gt; x + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lean prints &amp;lt;code&amp;gt;fun x =&amp;gt; x + 1 : Nat → Nat&amp;lt;/code&amp;gt;, although nothing in the text says &amp;lt;code&amp;gt;Nat&amp;lt;/code&amp;gt;. The type of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is unknown, so the instance problem for &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; stays stuck through every round. At the default step, Lean&amp;#039;s prelude marks &amp;lt;code&amp;gt;instHAdd&amp;lt;/code&amp;gt; as a default instance, which makes both summands and the result share one type, and marks &amp;lt;code&amp;gt;instOfNatNat&amp;lt;/code&amp;gt; as a low-priority default instance for numerals, which makes that type &amp;lt;code&amp;gt;Nat&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-prelude&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Init/Prelude.lean&amp;lt;/code&amp;gt;: the attributes &amp;lt;code&amp;gt;@[default_instance]&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;instHAdd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;@[default_instance 100]&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;instOfNatNat&amp;lt;/code&amp;gt;. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Init/Prelude.lean&amp;lt;/ref&amp;gt; Where no default applies, the elaborator reports the gap. Writing &amp;lt;code&amp;gt;def g := fun x =&amp;gt; x&amp;lt;/code&amp;gt; gives &amp;lt;code&amp;gt;Failed to infer type of binder `x`&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Why the elaborator is not trusted ==&lt;br /&gt;
&lt;br /&gt;
The elaborator is large, it is written in Lean, and users extend it: every new macro, elaborator or tactic becomes part of it.&amp;lt;ref name=&amp;quot;lean4-paper&amp;quot; /&amp;gt; The design does not ask anyone to trust it. Its output is checked by the Lean kernel, and the reference manual gives the reason: to guard against potential bugs in elaboration.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; The Lean kernel does no unification of its own and no termination checking; it expects the elaborator to have done that work, and checks the explicit result.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; As long as its output goes through the Lean kernel, a faulty elaborator can therefore cause a rejection, or a slow check, but not the acceptance of a false theorem. [[Lean theorem prover/System/The Lean kernel|The Lean kernel]] describes the checker.&lt;br /&gt;
&lt;br /&gt;
One thing the Lean kernel cannot protect is meaning. The elaborator decides what a statement says: which instance &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; refers to, which coercion was inserted, which type a numeral was given. The Lean kernel then checks a proof of &amp;#039;&amp;#039;that&amp;#039;&amp;#039; statement. The reference manual&amp;#039;s guidance on validating proofs makes the same separation: where there is doubt that a theorem means what it appears to mean, its statement and every definition it refers to must be examined, particularly where custom notation or type classes are involved.&amp;lt;ref name=&amp;quot;manual-validating&amp;quot;&amp;gt;Lean Developers, &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039;, version 4.34.0, &amp;quot;Validating a Lean Proof&amp;quot;. https://lean-lang.org/doc/reference/4.34.0/ValidatingProofs/&amp;lt;/ref&amp;gt; &amp;lt;code&amp;gt;pp.all&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;#print&amp;lt;/code&amp;gt; are the tools for that reading.&lt;br /&gt;
&lt;br /&gt;
== Beyond terms ==&lt;br /&gt;
&lt;br /&gt;
Elaboration also records what it did. Info trees relate the elaborated terms to the user&amp;#039;s original syntax, and an editor uses them to show types on hover and goals in a proof.&amp;lt;ref name=&amp;quot;manual-elab&amp;quot; /&amp;gt; Commands are elaborated too. A command elaborator may add definitions, open namespaces or declare new syntax, and these are changes to Lean&amp;#039;s state, not terms. This extensibility, a parser, elaborator and tactic framework that users can extend from Lean itself, is a design aim stated in the abstract and the conclusion of the paper that introduced Lean 4.&amp;lt;ref name=&amp;quot;lean4-paper&amp;quot;&amp;gt;Leonardo de Moura and Sebastian Ullrich, &amp;quot;The Lean 4 Theorem Prover and Programming Language (System Description)&amp;quot;, in &amp;#039;&amp;#039;Automated Deduction – CADE 28&amp;#039;&amp;#039;, Lecture Notes in Computer Science 12699, Springer, 2021, pp. 625–635. doi:10.1007/978-3-030-79876-5_37&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Next ==&lt;br /&gt;
&lt;br /&gt;
[[Lean theorem prover/System/The Lean kernel|The Lean kernel]] describes what happens to the elaborated term. [[Lean theorem prover/System/Metavariables and unification|Metavariables and unification]] opens up the placeholders used above.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>PMA Publisher</name></author>
	</entry>
</feed>