<?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%2FMetavariables_and_unification</id>
	<title>Lean theorem prover/System/Metavariables and unification - 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%2FMetavariables_and_unification"/>
	<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Metavariables_and_unification&amp;action=history"/>
	<updated>2026-10-21T07:32:50Z</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/Metavariables_and_unification&amp;diff=6788&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/Metavariables_and_unification&amp;diff=6788&amp;oldid=prev"/>
		<updated>2026-10-15T19:35:07Z</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:Metavariables and unification}}&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:Metavariables and unification}}&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/Holes and goals|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/Holes and goals|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;A &amp;#039;&amp;#039;&amp;#039;metavariable&amp;#039;&amp;#039;&amp;#039; is a named hole in a term: a placeholder for a subterm not yet known. &amp;#039;&amp;#039;&amp;#039;Unification&amp;#039;&amp;#039;&amp;#039; is the search for values of metavariables that make two terms definitionally equal. Lean&amp;#039;s elaborator, its tactic framework, its instance resolution and its simplifier all rest on these two things. This page describes them as Lean 4.34.1 implements them, explains why unification in Lean&amp;#039;s type theory cannot be solved in general, and says what Lean does instead and what its failures mean.&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;A &amp;#039;&amp;#039;&amp;#039;metavariable&amp;#039;&amp;#039;&amp;#039; is a named hole in a term: a placeholder for a subterm not yet known. &amp;#039;&amp;#039;&amp;#039;Unification&amp;#039;&amp;#039;&amp;#039; is the search for values of metavariables that make two terms definitionally equal. Lean&amp;#039;s elaborator, its tactic framework, its instance resolution and its simplifier all rest on these two things. This page describes them as Lean 4.34.1 implements them, explains why unification in Lean&amp;#039;s type theory cannot be solved in general, and says what Lean does instead and what its failures mean.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key pma_cutover_20261020t051351z-soft_:diff:1.41:old-6581:rev-6788:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>PMA Publisher</name></author>
	</entry>
	<entry>
		<id>https://invarians.org/index.php?title=Lean_theorem_prover/System/Metavariables_and_unification&amp;diff=6581&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/Metavariables_and_unification&amp;diff=6581&amp;oldid=prev"/>
		<updated>2026-10-14T18:01:05Z</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:Metavariables and unification}}&lt;br /&gt;
{{Plebeian|Lean theorem prover/Explainers/Holes and goals|Lean theorem prover/Explainers/When Lean says no}}&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;metavariable&amp;#039;&amp;#039;&amp;#039; is a named hole in a term: a placeholder for a subterm not yet known. &amp;#039;&amp;#039;&amp;#039;Unification&amp;#039;&amp;#039;&amp;#039; is the search for values of metavariables that make two terms definitionally equal. Lean&amp;#039;s elaborator, its tactic framework, its instance resolution and its simplifier all rest on these two things. This page describes them as Lean 4.34.1 implements them, explains why unification in Lean&amp;#039;s type theory cannot be solved in general, and says what Lean does instead and what its failures mean.&lt;br /&gt;
&lt;br /&gt;
== Holes with contexts ==&lt;br /&gt;
&lt;br /&gt;
When the elaborator meets &amp;lt;code&amp;gt;id 5&amp;lt;/code&amp;gt;, it does not yet know the implicit type argument of &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;. It creates a metavariable, written &amp;lt;code&amp;gt;?α&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;?m.7&amp;lt;/code&amp;gt; in Lean&amp;#039;s output, applies &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to it, and continues; the numeral &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt; must have type &amp;lt;code&amp;gt;?α&amp;lt;/code&amp;gt;, and the instance problem it brings, &amp;lt;code&amp;gt;OfNat ?α 5&amp;lt;/code&amp;gt;, later fixes &amp;lt;code&amp;gt;?α := Nat&amp;lt;/code&amp;gt; through a default instance ([[Lean theorem prover/System/Elaboration|elaboration]]). A goal in a tactic proof is the same thing: an unassigned metavariable whose type is the proposition to prove.&amp;lt;ref name=&amp;quot;mpbook&amp;quot;&amp;gt;Arthur Paulino, Damiano Testa, Edward Ayers, Evgenia Karunus, Henrik Böving, Jannis Limperg, Siddhartha Gadgil and Siddharth Bhat, &amp;#039;&amp;#039;Metaprogramming in Lean 4&amp;#039;&amp;#039;, online book, chapter &amp;quot;MetaM&amp;quot;. https://leanprover-community.github.io/lean4-metaprogramming-book/main/04_metam.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each metavariable is declared in a &amp;#039;&amp;#039;&amp;#039;local context&amp;#039;&amp;#039;&amp;#039;, the list of variables and hypotheses in scope where the hole was made, and it may be assigned only a term whose free variables come from that context. Lean&amp;#039;s source writes this &amp;lt;code&amp;gt;C |- ?m : t&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-defeq&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Lean/Meta/ExprDefEq.lean&amp;lt;/code&amp;gt;: the notes on pattern unification and its approximations A1 to A7. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/Meta/ExprDefEq.lean&amp;lt;/ref&amp;gt; The restriction is what keeps a solution from capturing a variable that did not exist where the hole was made.&lt;br /&gt;
&lt;br /&gt;
The assignments live in a &amp;#039;&amp;#039;&amp;#039;metavariable context&amp;#039;&amp;#039;&amp;#039;, separate from the terms. Assigning &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; does not rewrite the terms that mention it; a later call to &amp;lt;code&amp;gt;instantiateMVars&amp;lt;/code&amp;gt; substitutes the current assignments.&amp;lt;ref name=&amp;quot;mpbook&amp;quot; /&amp;gt; A term handed to the Lean kernel must contain no metavariables at all ([[Lean theorem prover/System/The Lean kernel|the Lean kernel]]).&lt;br /&gt;
&lt;br /&gt;
== Three kinds ==&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;
import Lean&lt;br /&gt;
#print Lean.MetavarKind&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;
inductive Lean.MetavarKind : Type&lt;br /&gt;
number of parameters: 0&lt;br /&gt;
constructors:&lt;br /&gt;
Lean.MetavarKind.natural : Lean.MetavarKind&lt;br /&gt;
Lean.MetavarKind.synthetic : Lean.MetavarKind&lt;br /&gt;
Lean.MetavarKind.syntheticOpaque : Lean.MetavarKind&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The design notes in Lean&amp;#039;s source give the rules. A &amp;#039;&amp;#039;&amp;#039;natural&amp;#039;&amp;#039;&amp;#039; metavariable may be assigned freely by unification; implicit arguments are of this kind. A &amp;#039;&amp;#039;&amp;#039;synthetic&amp;#039;&amp;#039;&amp;#039; one may be assigned by unification, but unification avoids doing so when it has a choice, because another procedure, usually instance resolution, is responsible for it and must check any value unification chose. A &amp;#039;&amp;#039;&amp;#039;synthetic opaque&amp;#039;&amp;#039;&amp;#039; one is never assigned by unification. Tactics such as &amp;lt;code&amp;gt;intro&amp;lt;/code&amp;gt; make their new goals of this kind: a goal counts as solved when its metavariable is assigned, so unification must not close it behind the user&amp;#039;s back.&amp;lt;ref name=&amp;quot;src-mctx&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Lean/MetavarContext.lean&amp;lt;/code&amp;gt;: the design notes at the head of the file. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/MetavarContext.lean&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Metavariables also carry a &amp;#039;&amp;#039;&amp;#039;depth&amp;#039;&amp;#039;&amp;#039;. A nested procedure, such as instance resolution called from inside unification, raises the depth of the metavariable context, and while it runs, unification does not assign metavariables of lower depth. This lets a subsidiary search create and solve its own holes without committing its caller to anything.&amp;lt;ref name=&amp;quot;src-mctx&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Delayed assignment ==&lt;br /&gt;
&lt;br /&gt;
A difficulty arises when a term containing a hole is put under a binder. Suppose &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; may mention &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and the elaborator forms &amp;lt;code&amp;gt;fun x =&amp;gt; t[?m]&amp;lt;/code&amp;gt;. If &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; is later assigned a term containing &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, that occurrence must refer to the new bound variable. Lean handles this by creating a fresh metavariable &amp;lt;code&amp;gt;?n&amp;lt;/code&amp;gt; of function type, with &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; removed from its context, and producing &amp;lt;code&amp;gt;fun x =&amp;gt; t[?n x]&amp;lt;/code&amp;gt;. For a natural or synthetic &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; it simply assigns &amp;lt;code&amp;gt;?m := ?n x&amp;lt;/code&amp;gt;. For a synthetic opaque &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt;, which may not be assigned that way, it records a &amp;#039;&amp;#039;&amp;#039;delayed assignment&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;?n #[x] := ?m&amp;lt;/code&amp;gt;: once &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; receives a value with no metavariables left in it, each &amp;lt;code&amp;gt;?n r&amp;lt;/code&amp;gt; is replaced by that value with &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-mctx&amp;quot; /&amp;gt; This is how a tactic block inside a &amp;lt;code&amp;gt;fun&amp;lt;/code&amp;gt; can be run after the surrounding term has been closed over its variables.&lt;br /&gt;
&lt;br /&gt;
== Unification ==&lt;br /&gt;
&lt;br /&gt;
The unifier is Lean&amp;#039;s definitional equality test with metavariables allowed. In the traces below it is written &amp;lt;code&amp;gt;s =?= t&amp;lt;/code&amp;gt;, and it runs in the monad &amp;lt;code&amp;gt;MetaM&amp;lt;/code&amp;gt; under a &amp;#039;&amp;#039;&amp;#039;transparency&amp;#039;&amp;#039;&amp;#039; setting that limits which definitions it may unfold, from &amp;lt;code&amp;gt;reducible&amp;lt;/code&amp;gt; through &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;all&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;mpbook&amp;quot; /&amp;gt; First-order cases are easy. To make &amp;lt;code&amp;gt;?n + 2&amp;lt;/code&amp;gt; equal to &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;, Lean solves &amp;lt;code&amp;gt;?n := 3&amp;lt;/code&amp;gt;:&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;
theorem w : ∃ n : Nat, n + 2 = 5 := ⟨_, rfl⟩&lt;br /&gt;
#print w&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This prints &amp;lt;code&amp;gt;Exists.intro 3 rfl&amp;lt;/code&amp;gt; as the proof. The hard cases have a metavariable applied to arguments, &amp;lt;code&amp;gt;?f a =?= t&amp;lt;/code&amp;gt;: then &amp;lt;code&amp;gt;?f&amp;lt;/code&amp;gt; stands for a function, and the problem is &amp;#039;&amp;#039;&amp;#039;higher-order unification&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Why it cannot be solved in general ==&lt;br /&gt;
&lt;br /&gt;
Higher-order unification is undecidable. Gérard Huet&amp;#039;s 1975 procedure for the typed λ-calculus is a semi-decision algorithm: it finds a unifier when one exists, but on problems with no unifier it may never stop, and Huet cites the undecidability of the problem as the reason.&amp;lt;ref name=&amp;quot;huet&amp;quot;&amp;gt;Gérard P. Huet, &amp;quot;A unification algorithm for typed λ-calculus&amp;quot;, &amp;#039;&amp;#039;Theoretical Computer Science&amp;#039;&amp;#039; 1(1), 1975, pp. 27–57. doi:10.1016/0304-3975(75)90011-0&amp;lt;/ref&amp;gt; For simply typed λ-terms it is undecidable already from order 2 upwards, a result due to Warren Goldfarb in 1981.&amp;lt;ref name=&amp;quot;goldfarb&amp;quot;&amp;gt;Warren D. Goldfarb, &amp;quot;The undecidability of the second-order unification problem&amp;quot;, &amp;#039;&amp;#039;Theoretical Computer Science&amp;#039;&amp;#039; 13(2), 1981, pp. 225–230. doi:10.1016/0304-3975(81)90040-2. Not consulted directly; the result is stated, with this citation, in the introduction of Miller 1991.&amp;lt;/ref&amp;gt; Unifiers are also not unique: &amp;lt;code&amp;gt;?f 3 =?= 3 + 3&amp;lt;/code&amp;gt; is solved by &amp;lt;code&amp;gt;fun x =&amp;gt; x + x&amp;lt;/code&amp;gt;, by &amp;lt;code&amp;gt;fun x =&amp;gt; 3 + x&amp;lt;/code&amp;gt;, by &amp;lt;code&amp;gt;fun x =&amp;gt; x + 3&amp;lt;/code&amp;gt; and by the constant function returning &amp;lt;code&amp;gt;3 + 3&amp;lt;/code&amp;gt;, and none is more general than the others.&lt;br /&gt;
&lt;br /&gt;
Dale Miller isolated a fragment that behaves well. In a &amp;#039;&amp;#039;&amp;#039;pattern&amp;#039;&amp;#039;&amp;#039;, a metavariable is applied only to distinct bound variables. Unification restricted to patterns is decidable and has most general unifiers whenever it has unifiers at all.&amp;lt;ref name=&amp;quot;miller&amp;quot;&amp;gt;Dale Miller, &amp;quot;A logic programming language with lambda-abstraction, function variables, and simple unification&amp;quot;, &amp;#039;&amp;#039;Journal of Logic and Computation&amp;#039;&amp;#039; 1(4), 1991, pp. 497–536. doi:10.1093/logcom/1.4.497&amp;lt;/ref&amp;gt; The solution is read off directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;?m\ a_1 \ldots a_n =^{?} t \quad\Longrightarrow\quad ?m := \lambda a_1 \ldots a_n.\ t&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What Lean does ==&lt;br /&gt;
&lt;br /&gt;
Lean&amp;#039;s unifier, in &amp;lt;code&amp;gt;ExprDefEq.lean&amp;lt;/code&amp;gt;, states its own version of Miller&amp;#039;s condition. The constraint &amp;lt;code&amp;gt;?m@C a₁ … aₙ =?= t&amp;lt;/code&amp;gt; is a pattern when the &amp;lt;code&amp;gt;aᵢ&amp;lt;/code&amp;gt; are pairwise distinct free variables that are not local definitions and are not in &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;, when &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; uses only variables from &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;aᵢ&amp;lt;/code&amp;gt;, when every metavariable in &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; has a context that is a prefix of &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;, and when &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; does not occur in &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt;. Then &amp;lt;code&amp;gt;?m := fun a₁ … aₙ =&amp;gt; t&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-defeq&amp;quot; /&amp;gt; The following problem is a pattern, because &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is bound after the hole for &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; was made:&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;
theorem dbl : ∃ f : Nat → Nat, ∀ x, f x = x * 2 := ⟨_, fun _ =&amp;gt; rfl⟩&lt;br /&gt;
#print dbl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof printed is &amp;lt;code&amp;gt;Exists.intro (fun x =&amp;gt; x * 2) fun x =&amp;gt; rfl&amp;lt;/code&amp;gt;. With &amp;lt;code&amp;gt;set_option trace.Meta.isDefEq.assign true&amp;lt;/code&amp;gt; the unifier reports the step, among 67 lines of trace:&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;
[Meta.isDefEq.assign] ✅️ ?m.12 x✝ := x✝ * 2&lt;br /&gt;
  [Meta.isDefEq.assign.beforeMkLambda] ?m.12 [x✝] := x✝ * 2&lt;br /&gt;
  [Meta.isDefEq.assign.checkTypes] ✅️ (?m.12 : Nat → Nat) := (fun x =&amp;gt; x * 2 : Nat → Nat)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Outside the pattern fragment Lean does not search, as Huet&amp;#039;s procedure would. It applies workarounds and approximations, listed in the source as A1 to A7. Among them: &amp;#039;&amp;#039;&amp;#039;first-order approximation&amp;#039;&amp;#039;&amp;#039;, which solves &amp;lt;code&amp;gt;?m a₁ … aₙ =?= f b₁ … bₖ&amp;lt;/code&amp;gt; by matching the last arguments pairwise and unifying &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; applied to the rest with &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; applied to the rest; and &amp;#039;&amp;#039;&amp;#039;constant approximation&amp;#039;&amp;#039;&amp;#039;, which assigns &amp;lt;code&amp;gt;?m&amp;lt;/code&amp;gt; a function ignoring its arguments. The notes say plainly that constant approximation may produce bad solutions, and give an example on which another of the approximations fails.&amp;lt;ref name=&amp;quot;src-defeq&amp;quot; /&amp;gt; Each picks one unifier and commits to it:&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;
theorem d (h : (3 : Nat) = 3) : 3 + 3 = 3 + 3 := congrArg _ h&lt;br /&gt;
#print d&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conclusion of &amp;lt;code&amp;gt;congrArg&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;?f ?x = ?f ?y&amp;lt;/code&amp;gt;, and the elaborator unifies it with the expected type before it looks at &amp;lt;code&amp;gt;h&amp;lt;/code&amp;gt;. So the first constraint is &amp;lt;code&amp;gt;?f ?x =?= 3 + 3&amp;lt;/code&amp;gt;, whose argument is itself unknown: not a pattern. The trace shows first-order approximation at work. It reads &amp;lt;code&amp;gt;3 + 3&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;HAdd.hAdd 3 3&amp;lt;/code&amp;gt;, matches the last argument, &amp;lt;code&amp;gt;?x := 3&amp;lt;/code&amp;gt;, and the rest, &amp;lt;code&amp;gt;?f := HAdd.hAdd 3&amp;lt;/code&amp;gt;. The proof printed is &amp;lt;code&amp;gt;fun h =&amp;gt; congrArg (HAdd.hAdd 3) h&amp;lt;/code&amp;gt;: of the four solutions above, the one that reads &amp;lt;code&amp;gt;3 + x&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== When it fails ==&lt;br /&gt;
&lt;br /&gt;
The same approximation can choose wrongly. Here &amp;lt;code&amp;gt;?f a =?= a * 2&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;?f b =?= b * 2&amp;lt;/code&amp;gt; must both hold, and &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; belongs to the context in which &amp;lt;code&amp;gt;?f&amp;lt;/code&amp;gt; was made, so the first constraint is not a pattern:&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;
example (a b : Nat) (h : a = b) : a * 2 = b * 2 := congrArg _ h&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;
error: Type mismatch&lt;br /&gt;
  congrArg ?m.19 h&lt;br /&gt;
has type&lt;br /&gt;
  ?m.19 a = ?m.19 b&lt;br /&gt;
but is expected to have type&lt;br /&gt;
  a * 2 = b * 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The trace option &amp;lt;code&amp;gt;trace.Meta.isDefEq&amp;lt;/code&amp;gt; shows why. In this excerpt, lines marked … are omitted:&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;
[Meta.isDefEq] ❌️ a * 2 = b * 2 =?= ?m.19 a = ?m.19 b&lt;br /&gt;
  …&lt;br /&gt;
  [Meta.isDefEq] ✅️ a * 2 =?= ?m.19 a&lt;br /&gt;
    [Meta.isDefEq] a * 2 [nonassignable] =?= ?m.19 a [assignable]&lt;br /&gt;
    [Meta.isDefEq.foApprox] ?m.19 [a] := a * 2&lt;br /&gt;
    [Meta.isDefEq] ❌️ a =?= 2&lt;br /&gt;
    …&lt;br /&gt;
    [Meta.isDefEq.foApprox] ?m.19 [a] := (a.mul 1).add a&lt;br /&gt;
    [Meta.isDefEq] ✅️ a =?= a&lt;br /&gt;
    [Meta.isDefEq] ✅️ ?m.19 =?= (a.mul 1).add&lt;br /&gt;
  …&lt;br /&gt;
  [Meta.isDefEq] ❌️ b * 2 =?= (a.mul 1).add b&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First-order approximation first tries to match &amp;lt;code&amp;gt;a * 2&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;?m.19 a&amp;lt;/code&amp;gt; and fails on &amp;lt;code&amp;gt;a =?= 2&amp;lt;/code&amp;gt;. After unfolding multiplication it succeeds, with &amp;lt;code&amp;gt;?m.19 := (a.mul 1).add&amp;lt;/code&amp;gt;, a function that satisfies the first constraint and not the second. Writing the function explicitly, &amp;lt;code&amp;gt;congrArg (· * 2) h&amp;lt;/code&amp;gt;, removes the higher-order problem and the proof is accepted.&lt;br /&gt;
&lt;br /&gt;
A message such as &amp;lt;code&amp;gt;Type mismatch&amp;lt;/code&amp;gt;, or from a tactic &amp;lt;code&amp;gt;could not unify&amp;lt;/code&amp;gt;, therefore reports that the unifier returned false. That has three possible causes. The terms may differ under every assignment. The unifier may have committed to a wrong approximation, as above, or met a problem outside what it attempts. Or the transparency setting may have forbidden an unfolding that would have shown the terms equal. Only the first is a fact about the mathematics. The cure for the other two is to supply the missing information: an explicit argument, a type ascription, or a lemma that rewrites one side into the shape of the other.&lt;br /&gt;
&lt;br /&gt;
== Next ==&lt;br /&gt;
&lt;br /&gt;
[[Lean theorem prover/System/Type classes and instance resolution|Type classes and instance resolution]] describes the search that fills synthetic metavariables, and [[Lean theorem prover/System/The tactic framework|the tactic framework]] the one that fills goals.&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>