<?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%2FThe_tactic_framework</id>
	<title>Lean theorem prover/System/The tactic framework - 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%2FThe_tactic_framework"/>
	<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/The_tactic_framework&amp;action=history"/>
	<updated>2026-10-21T09:16:04Z</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/The_tactic_framework&amp;diff=6790&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/The_tactic_framework&amp;diff=6790&amp;oldid=prev"/>
		<updated>2026-10-15T19:35:10Z</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:The tactic framework}}&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:The tactic framework}}&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/Tactics|Lean theorem prover/Explainers/Holes and goals}}&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/Tactics|Lean theorem prover/Explainers/Holes and goals}}&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;tactic&amp;#039;&amp;#039;&amp;#039; is a program that constructs part of a proof term. The &amp;#039;&amp;#039;&amp;#039;tactic framework&amp;#039;&amp;#039;&amp;#039; is what such programs act on: a list of goals, each one a metavariable, inside a monad that gives access to everything the elaborator can do. This page describes the framework in Lean 4.34.1: how a &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; block enters elaboration, what a goal is, the monad &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;, the combinators that join tactics, a new tactic defined in three lines, and the library-search tactics &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;apply?&amp;lt;/code&amp;gt;. The idea of a tactic goes back to the LCF system; [[Lean theorem prover/History/LCF and the tactic idea|LCF and the tactic idea]] tells that history.&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;tactic&amp;#039;&amp;#039;&amp;#039; is a program that constructs part of a proof term. The &amp;#039;&amp;#039;&amp;#039;tactic framework&amp;#039;&amp;#039;&amp;#039; is what such programs act on: a list of goals, each one a metavariable, inside a monad that gives access to everything the elaborator can do. This page describes the framework in Lean 4.34.1: how a &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; block enters elaboration, what a goal is, the monad &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;, the combinators that join tactics, a new tactic defined in three lines, and the library-search tactics &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;apply?&amp;lt;/code&amp;gt;. The idea of a tactic goes back to the LCF system; [[Lean theorem prover/History/LCF and the tactic idea|LCF and the tactic idea]] tells that history.&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/The_tactic_framework&amp;diff=6583&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/The_tactic_framework&amp;diff=6583&amp;oldid=prev"/>
		<updated>2026-10-14T18:01:06Z</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:The tactic framework}}&lt;br /&gt;
{{Plebeian|Lean theorem prover/Explainers/Tactics|Lean theorem prover/Explainers/Holes and goals}}&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;tactic&amp;#039;&amp;#039;&amp;#039; is a program that constructs part of a proof term. The &amp;#039;&amp;#039;&amp;#039;tactic framework&amp;#039;&amp;#039;&amp;#039; is what such programs act on: a list of goals, each one a metavariable, inside a monad that gives access to everything the elaborator can do. This page describes the framework in Lean 4.34.1: how a &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; block enters elaboration, what a goal is, the monad &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;, the combinators that join tactics, a new tactic defined in three lines, and the library-search tactics &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;apply?&amp;lt;/code&amp;gt;. The idea of a tactic goes back to the LCF system; [[Lean theorem prover/History/LCF and the tactic idea|LCF and the tactic idea]] tells that history.&lt;br /&gt;
&lt;br /&gt;
== A by block is a hole ==&lt;br /&gt;
&lt;br /&gt;
The reference manual states that &amp;lt;code&amp;gt;by tac&amp;lt;/code&amp;gt; constructs a term of the expected type by running the tactics &amp;lt;code&amp;gt;tac&amp;lt;/code&amp;gt;, and that tactics construct proof terms behind the scenes.&amp;lt;ref name=&amp;quot;manual-tactics&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;Tactic Proofs&amp;quot;, with its section &amp;quot;Running Tactics&amp;quot;. https://lean-lang.org/doc/reference/4.34.0/Tactic-Proofs/&amp;lt;/ref&amp;gt; When the elaborator meets &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt;, it does not run the tactics at once. It creates a metavariable of the expected type, of the synthetic opaque kind, and registers the tactic code to be run later.&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;mkTacticMVar&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; Tactic blocks are run in the last round of pending work, after the rest of the surrounding term has been elaborated as far as it can be, so that the goal&amp;#039;s type is as complete as possible.&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; The trace option &amp;lt;code&amp;gt;trace.Elab.step&amp;lt;/code&amp;gt; shows both moments. These lines are an excerpt from the trace of &amp;lt;code&amp;gt;example (p : Prop) (hp : p) : p ∧ p := by exact ⟨hp, hp⟩&amp;lt;/code&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;
[Elab.step] ✅️ expected type: p ∧ p, term&lt;br /&gt;
    by exact ⟨hp, hp⟩&lt;br /&gt;
  [Elab.step.result] ?m.1&lt;br /&gt;
[Elab.step] ✅️ exact ⟨hp, hp⟩&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; term elaborates to the metavariable &amp;lt;code&amp;gt;?m.1&amp;lt;/code&amp;gt;. Only afterwards does the tactic &amp;lt;code&amp;gt;exact&amp;lt;/code&amp;gt; run, and it is the tactic that assigns &amp;lt;code&amp;gt;?m.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Goals are metavariables ==&lt;br /&gt;
&lt;br /&gt;
The state of the tactic framework is a structure with one field, &amp;lt;code&amp;gt;goals : List MVarId&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-tactic&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Lean/Elab/Tactic/Basic.lean&amp;lt;/code&amp;gt;: the definitions &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tactic&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;getGoals&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tryCatchRestore&amp;lt;/code&amp;gt;, with the structure &amp;lt;code&amp;gt;Tactic.State&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;src/Lean/Elab/Term/TermElabM.lean&amp;lt;/code&amp;gt;. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/Elab/Tactic/Basic.lean&amp;lt;/ref&amp;gt; Each goal is a metavariable, with its own local context of hypotheses and its type, the statement still to be proved; the first is the &amp;#039;&amp;#039;&amp;#039;main goal&amp;#039;&amp;#039;&amp;#039;.&amp;lt;ref name=&amp;quot;manual-tactics&amp;quot; /&amp;gt; A tactic usually acts on the main goal: it assigns that metavariable a term, which may contain new metavariables, and puts the new metavariables in the goal&amp;#039;s place. The goal of a &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; block, and the goals made by tactics such as &amp;lt;code&amp;gt;intro&amp;lt;/code&amp;gt;, are synthetic opaque, the kind that unification never assigns, because a goal counts as solved exactly when its metavariable is assigned.&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; [[Lean theorem prover/System/Metavariables and unification|Metavariables and unification]] treats these kinds.&lt;br /&gt;
&lt;br /&gt;
A short tactic that reads the list makes the point concrete:&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;
open Lean Elab Tactic in&lt;br /&gt;
elab &amp;quot;show_goals&amp;quot; : tactic =&amp;gt; do&lt;br /&gt;
  let gs ← getGoals&lt;br /&gt;
  logInfo m!&amp;quot;goals: {gs.map Expr.mvar}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
example (p q : Prop) (hp : p) (hq : q) : p ∧ q := by&lt;br /&gt;
  constructor&lt;br /&gt;
  show_goals&lt;br /&gt;
  all_goals assumption&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It prints &amp;lt;code&amp;gt;goals: [?left, ?right]&amp;lt;/code&amp;gt;. The tactic &amp;lt;code&amp;gt;constructor&amp;lt;/code&amp;gt; assigned the original goal the term &amp;lt;code&amp;gt;And.intro ?left ?right&amp;lt;/code&amp;gt; and left the two new metavariables as goals, named after the arguments of &amp;lt;code&amp;gt;And.intro&amp;lt;/code&amp;gt;. The built-in &amp;lt;code&amp;gt;trace_state&amp;lt;/code&amp;gt; prints the same goals in the familiar form:&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 and_swap (p q : Prop) (h : p ∧ q) : q ∧ p := by&lt;br /&gt;
  constructor&lt;br /&gt;
  trace_state&lt;br /&gt;
  · exact h.2&lt;br /&gt;
  · exact h.1&lt;br /&gt;
&lt;br /&gt;
#print and_swap&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;
case left&lt;br /&gt;
p q : Prop&lt;br /&gt;
h : p ∧ q&lt;br /&gt;
⊢ q&lt;br /&gt;
&lt;br /&gt;
case right&lt;br /&gt;
p q : Prop&lt;br /&gt;
h : p ∧ q&lt;br /&gt;
⊢ p&lt;br /&gt;
theorem and_swap : ∀ (p q : Prop), p ∧ q → q ∧ p :=&lt;br /&gt;
fun p q h =&amp;gt; ⟨h.right, h.left⟩&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The last lines are the point of the whole framework. Once every goal is assigned, the &amp;lt;code&amp;gt;by&amp;lt;/code&amp;gt; block&amp;#039;s metavariable stands for an ordinary term, and that term, not the script, is what the Lean kernel checks and what Lean&amp;#039;s environment, its table of definitions and theorems, records. If a goal is left open, the block 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;
example (p q : Prop) (hp : p) : p ∧ q := by&lt;br /&gt;
  constructor&lt;br /&gt;
  exact hp&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: unsolved goals&lt;br /&gt;
case right&lt;br /&gt;
p q : Prop&lt;br /&gt;
hp : p&lt;br /&gt;
⊢ q&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The monad TacticM ==&lt;br /&gt;
&lt;br /&gt;
In Lean 4.34.1 the tactic monad is defined as&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;
abbrev TacticM := ReaderT Context $ StateRefT State TermElabM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Its documentation describes it as the term elaboration monad &amp;lt;code&amp;gt;TermElabM&amp;lt;/code&amp;gt; extended with the current goals and some local information, and defines a tactic as a function from syntax to an action in &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;. Several tactics may be attached to one kind of syntax; they are attempted until one succeeds.&amp;lt;ref name=&amp;quot;src-tactic&amp;quot; /&amp;gt; Because &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt; sits on top of the elaborator, a tactic can do anything the elaborator does: elaborate a term against a goal, call the unifier, search for instances, inspect and extend Lean&amp;#039;s environment. [[Lean theorem prover/System/Monads and do-notation|Monads and do-notation]] explains the monad stack, and [[Lean theorem prover/System/Metaprogramming in Lean|metaprogramming in Lean]] shows how to write tactics against it. The paper that introduced Lean 4 stresses that tactics are implemented in Lean itself and can be added by users.&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;
None of this is trusted. A tactic may be wrong in any way: it may assign a goal an ill-typed term, or a term of the wrong type. The error surfaces when the term is checked, at the latest by the Lean kernel ([[Lean theorem prover/System/The Lean kernel|the Lean kernel]]).&lt;br /&gt;
&lt;br /&gt;
== Combinators ==&lt;br /&gt;
&lt;br /&gt;
Tactics are joined by combinators, themselves tactics. Their documentation strings in Lean&amp;#039;s source give their meaning:&amp;lt;ref name=&amp;quot;src-tactics&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), file &amp;lt;code&amp;gt;src/Init/Tactics.lean&amp;lt;/code&amp;gt;: the documentation strings and definitions of &amp;lt;code&amp;gt;all_goals&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;focus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;first&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;try&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;lt;;&amp;amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;repeat&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;apply?&amp;lt;/code&amp;gt;. Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Init/Tactics.lean&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t₁ &amp;amp;lt;;&amp;amp;gt; t₂&amp;lt;/code&amp;gt; runs &amp;lt;code&amp;gt;t₁&amp;lt;/code&amp;gt; on the main goal and &amp;lt;code&amp;gt;t₂&amp;lt;/code&amp;gt; on each goal it produces, collecting all the goals that result.&lt;br /&gt;
* &amp;lt;code&amp;gt;all_goals t&amp;lt;/code&amp;gt; runs &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; on every goal and fails if it fails on any. &amp;lt;code&amp;gt;focus t&amp;lt;/code&amp;gt; runs &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; on the main goal with the other goals hidden; the bullet &amp;lt;code&amp;gt;· t&amp;lt;/code&amp;gt;, used in &amp;lt;code&amp;gt;and_swap&amp;lt;/code&amp;gt; above and preferred by the documentation, also requires that &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; close the goal. In 4.34.1 &amp;lt;code&amp;gt;t₁ &amp;amp;lt;;&amp;amp;gt; t₂&amp;lt;/code&amp;gt; is itself a macro built from &amp;lt;code&amp;gt;focus&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;all_goals&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;first | t₁ | t₂ | …&amp;lt;/code&amp;gt; runs each alternative until one succeeds, and fails if none does.&lt;br /&gt;
* &amp;lt;code&amp;gt;try t&amp;lt;/code&amp;gt; runs &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; and succeeds even if &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; fails. In 4.34.1 it is not a primitive but a macro: &amp;lt;code&amp;gt;first | t | skip&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;repeat t&amp;lt;/code&amp;gt; applies &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; for as long as it succeeds, undoing any partial changes of the attempt that fails. If &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; never fails, &amp;lt;code&amp;gt;repeat t&amp;lt;/code&amp;gt; does not stop. &amp;lt;code&amp;gt;repeat&amp;#039; t&amp;lt;/code&amp;gt; applies &amp;lt;code&amp;gt;t&amp;lt;/code&amp;gt; recursively to every goal 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;
example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by&lt;br /&gt;
  constructor &amp;amp;lt;;&amp;amp;gt; (try constructor) &amp;amp;lt;;&amp;amp;gt; assumption&lt;br /&gt;
&lt;br /&gt;
example (n : Nat) : n = n ∨ n = 0 := by&lt;br /&gt;
  first&lt;br /&gt;
  | exact Or.inr rfl&lt;br /&gt;
  | exact Or.inl rfl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the second example the first alternative fails, since &amp;lt;code&amp;gt;n = 0&amp;lt;/code&amp;gt; does not hold for every &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;first&amp;lt;/code&amp;gt; backtracks to the second. Before the second alternative runs, the state saved before the first is restored, metavariable assignments included: in &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt; the standard way of catching a failure rolls the state back.&amp;lt;ref name=&amp;quot;src-tactic&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== A new tactic in three lines ==&lt;br /&gt;
&lt;br /&gt;
Since &amp;lt;code&amp;gt;try&amp;lt;/code&amp;gt; is a macro, so can a user&amp;#039;s tactic be. A &amp;#039;&amp;#039;&amp;#039;macro&amp;#039;&amp;#039;&amp;#039; tactic rewrites its own syntax into existing tactics, and needs no access to &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt; at all:&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;
macro &amp;quot;conj_close&amp;quot; : tactic =&amp;gt;&lt;br /&gt;
  `(tactic| (repeat&amp;#039; constructor) &amp;amp;lt;;&amp;amp;gt; assumption)&lt;br /&gt;
&lt;br /&gt;
theorem three (p q r : Prop) (hp : p) (hq : q) (hr : r) : (p ∧ q) ∧ r := by&lt;br /&gt;
  conj_close&lt;br /&gt;
&lt;br /&gt;
#print three&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;
theorem three : ∀ (p q r : Prop), p → q → r → (p ∧ q) ∧ r :=&lt;br /&gt;
fun p q r hp hq hr =&amp;gt; ⟨⟨hp, hq⟩, hr⟩&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new tactic &amp;lt;code&amp;gt;conj_close&amp;lt;/code&amp;gt; splits nested conjunctions as far as they go and closes every resulting goal with a hypothesis. The proof term shows nothing of it. [[Lean theorem prover/System/Syntax extension and hygienic macros|Syntax extension and hygienic macros]] covers the macro system, and [[Lean theorem prover/System/Metaprogramming in Lean|metaprogramming in Lean]] the tactics that need &amp;lt;code&amp;gt;elab&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;, as &amp;lt;code&amp;gt;show_goals&amp;lt;/code&amp;gt; above did.&lt;br /&gt;
&lt;br /&gt;
== Library search ==&lt;br /&gt;
&lt;br /&gt;
Lean 4.34.1 includes two search tactics in its core. &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; searches Lean&amp;#039;s environment for a definition or theorem that closes the goal with &amp;lt;code&amp;gt;exact&amp;lt;/code&amp;gt;, discharging side conditions with &amp;lt;code&amp;gt;solve_by_elim&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;apply?&amp;lt;/code&amp;gt; searches for one that refines the goal with &amp;lt;code&amp;gt;apply&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;src-tactics&amp;quot; /&amp;gt; Both report what they found as a suggestion to paste in place of the search:&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;
example (a b : Nat) : a + b = b + a := by&lt;br /&gt;
  exact?&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;
Try this:&lt;br /&gt;
  [apply] exact Nat.add_comm a b&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The suggestion is meant to replace the call. A proof that keeps &amp;lt;code&amp;gt;exact?&amp;lt;/code&amp;gt; repeats the search every time the file is checked, and may find a different lemma once the library changes.&lt;br /&gt;
&lt;br /&gt;
== Next ==&lt;br /&gt;
&lt;br /&gt;
[[Lean theorem prover/System/Decision procedures|Decision procedures]] describes the tactics that settle whole classes of goals: &amp;lt;code&amp;gt;decide&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;omega&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;simp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grind&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;bv_decide&amp;lt;/code&amp;gt;.&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>