<?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%2FMetaprogramming_in_Lean</id>
	<title>Lean theorem prover/System/Metaprogramming in Lean - 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%2FMetaprogramming_in_Lean"/>
	<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Metaprogramming_in_Lean&amp;action=history"/>
	<updated>2026-10-21T16:57:45Z</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/Metaprogramming_in_Lean&amp;diff=6793&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/Metaprogramming_in_Lean&amp;diff=6793&amp;oldid=prev"/>
		<updated>2026-10-15T19:35:15Z</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:Metaprogramming in Lean}}&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:Metaprogramming in Lean}}&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}}&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}}&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;metaprogram&amp;#039;&amp;#039;&amp;#039; is a Lean program whose data is Lean itself: syntax, terms, 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;A &amp;#039;&amp;#039;&amp;#039;metaprogram&amp;#039;&amp;#039;&amp;#039; is a Lean program whose data is Lean itself: syntax, terms, goals,&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/Metaprogramming_in_Lean&amp;diff=6586&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/Metaprogramming_in_Lean&amp;diff=6586&amp;oldid=prev"/>
		<updated>2026-10-14T18:01:08Z</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:Metaprogramming in Lean}}&lt;br /&gt;
{{Plebeian|Lean theorem prover/Explainers/Tactics}}&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;metaprogram&amp;#039;&amp;#039;&amp;#039; is a Lean program whose data is Lean itself: syntax, terms, goals,&lt;br /&gt;
Lean&amp;#039;s environment (its table of definitions and theorems). Tactics, elaborators and&lt;br /&gt;
custom commands are metaprograms. Lean 4 is written largely in Lean, and the data&lt;br /&gt;
structures and functions its own elaborator and tactics use are available to any Lean source&lt;br /&gt;
file that imports the library &amp;lt;code&amp;gt;Lean&amp;lt;/code&amp;gt;.&amp;lt;ref name=&amp;quot;cade&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:[https://doi.org/10.1007/978-3-030-79876-5_37 10.1007/978-3-030-79876-5_37]; author&amp;#039;s copy [https://lean-lang.org/papers/lean4.pdf lean-lang.org/papers/lean4.pdf].&amp;lt;/ref&amp;gt;&lt;br /&gt;
This page shows the representation of terms, the monads metaprograms run in, and three small&lt;br /&gt;
metaprograms compiled by Lean 4.34.1: a term elaborator, a command and a tactic. It ends with&lt;br /&gt;
the point on which the whole arrangement rests. A metaprogram is not trusted. What it&lt;br /&gt;
produces is checked by the Lean kernel, and a wrong result is rejected, unless that check has&lt;br /&gt;
been switched off.&lt;br /&gt;
&lt;br /&gt;
== Terms as data ==&lt;br /&gt;
&lt;br /&gt;
Every term that reaches the Lean kernel is a value of the inductive type&lt;br /&gt;
&amp;lt;code&amp;gt;Lean.Expr&amp;lt;/code&amp;gt;. Lean prints its definition on request.&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 Meta Elab Term Command Tactic&lt;br /&gt;
&lt;br /&gt;
#print Lean.Expr&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;inductive Lean.Expr : Type&lt;br /&gt;
number of parameters: 0&lt;br /&gt;
constructors:&lt;br /&gt;
Lean.Expr.bvar : Nat → Expr&lt;br /&gt;
Lean.Expr.fvar : FVarId → Expr&lt;br /&gt;
Lean.Expr.mvar : MVarId → Expr&lt;br /&gt;
Lean.Expr.sort : Level → Expr&lt;br /&gt;
Lean.Expr.const : Name → List Level → Expr&lt;br /&gt;
Lean.Expr.app : Expr → Expr → Expr&lt;br /&gt;
Lean.Expr.lam : Name → Expr → Expr → BinderInfo → Expr&lt;br /&gt;
Lean.Expr.forallE : Name → Expr → Expr → BinderInfo → Expr&lt;br /&gt;
Lean.Expr.letE : Name → Expr → Expr → Expr → Bool → Expr&lt;br /&gt;
Lean.Expr.lit : Literal → Expr&lt;br /&gt;
Lean.Expr.mdata : MData → Expr → Expr&lt;br /&gt;
Lean.Expr.proj : Name → Nat → Expr → Expr&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The twelve constructors are the type theory&amp;#039;s term formers with some additions for&lt;br /&gt;
practical use. &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; is a universe, &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt; a reference to a named&lt;br /&gt;
definition or theorem at given universe levels, &amp;lt;code&amp;gt;app&amp;lt;/code&amp;gt; an application,&lt;br /&gt;
&amp;lt;code&amp;gt;lam&amp;lt;/code&amp;gt; a function, &amp;lt;code&amp;gt;forallE&amp;lt;/code&amp;gt; a dependent function type and&lt;br /&gt;
&amp;lt;code&amp;gt;letE&amp;lt;/code&amp;gt; a local definition. &amp;lt;code&amp;gt;lit&amp;lt;/code&amp;gt; holds a natural-number or string&lt;br /&gt;
literal, &amp;lt;code&amp;gt;proj&amp;lt;/code&amp;gt; a projection from a structure, and &amp;lt;code&amp;gt;mdata&amp;lt;/code&amp;gt; attaches&lt;br /&gt;
metadata that does not change meaning. Three constructors are variables of different&lt;br /&gt;
kinds. &amp;lt;code&amp;gt;bvar&amp;lt;/code&amp;gt; is a variable bound inside the term, named by a number, the&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;de Bruijn index&amp;#039;&amp;#039;&amp;#039;, which counts binders outward; &amp;lt;code&amp;gt;fvar&amp;lt;/code&amp;gt; is a free variable&lt;br /&gt;
of the current local context, such as a hypothesis in a goal; &amp;lt;code&amp;gt;mvar&amp;lt;/code&amp;gt; is a&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;metavariable&amp;#039;&amp;#039;&amp;#039;, a hole still to be filled (see&lt;br /&gt;
[[Lean theorem prover/System/Metavariables and unification|metavariables and unification]]).&lt;br /&gt;
&lt;br /&gt;
A small definition shows the encoding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont&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;
def k : Nat → Nat → Nat := fun a _b =&amp;gt; a&lt;br /&gt;
&lt;br /&gt;
#eval show MetaM Unit from do&lt;br /&gt;
  let info ← getConstInfo ``k&lt;br /&gt;
  IO.println (repr info.value!)&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;Lean.Expr.lam&lt;br /&gt;
  `a&lt;br /&gt;
  (Lean.Expr.const `Nat [])&lt;br /&gt;
  (Lean.Expr.lam `_b (Lean.Expr.const `Nat []) (Lean.Expr.bvar 1) (Lean.BinderInfo.default))&lt;br /&gt;
  (Lean.BinderInfo.default)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The body of the inner function is &amp;lt;code&amp;gt;bvar 1&amp;lt;/code&amp;gt;: the variable bound one binder further&lt;br /&gt;
out than the nearest, which is &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;. Binder names are kept only for display: two&lt;br /&gt;
terms that differ only in the names of bound variables are treated as the same term.&lt;br /&gt;
&lt;br /&gt;
== The monads ==&lt;br /&gt;
&lt;br /&gt;
A metaprogram runs in a monad that gives it access to the state of Lean (see&lt;br /&gt;
[[Lean theorem prover/System/Monads and do-notation|monads and do-notation]] for monads in&lt;br /&gt;
general). There is a stack of four, each extending the one below it. Lean prints their&lt;br /&gt;
definitions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont&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;
#print CoreM&lt;br /&gt;
#print MetaM&lt;br /&gt;
#print TermElabM&lt;br /&gt;
#print TacticM&lt;br /&gt;
#print CommandElabM&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;@[reducible] def Lean.Core.CoreM : Type → Type :=&lt;br /&gt;
ReaderT Core.Context (StateRefT&amp;#039; IO.RealWorld Core.State (EIO Exception))&lt;br /&gt;
@[reducible] def Lean.Meta.MetaM : Type → Type :=&lt;br /&gt;
ReaderT Meta.Context (StateRefT&amp;#039; IO.RealWorld Meta.State CoreM)&lt;br /&gt;
@[reducible] def Lean.Elab.Term.TermElabM : Type → Type :=&lt;br /&gt;
ReaderT Term.Context (StateRefT&amp;#039; IO.RealWorld Term.State MetaM)&lt;br /&gt;
@[reducible] def Lean.Elab.Tactic.TacticM : Type → Type :=&lt;br /&gt;
ReaderT Tactic.Context (StateRefT&amp;#039; IO.RealWorld Tactic.State TermElabM)&lt;br /&gt;
@[reducible] def Lean.Elab.Command.CommandElabM : Type → Type :=&lt;br /&gt;
ReaderT Command.Context (StateRefT&amp;#039; IO.RealWorld Command.State (EIO Exception))&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each layer adds a read-only context (&amp;lt;code&amp;gt;ReaderT&amp;lt;/code&amp;gt;) and a mutable state&lt;br /&gt;
(&amp;lt;code&amp;gt;StateRefT&amp;#039;&amp;lt;/code&amp;gt;) on top of the layer below. At the bottom, &amp;lt;code&amp;gt;EIO&lt;br /&gt;
Exception&amp;lt;/code&amp;gt; is input and output that can fail with an exception, so every metaprogram&lt;br /&gt;
can do input and output.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;CoreM&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; gives access to Lean&amp;#039;s environment: what has been defined or imported at the current point.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;MetaM&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; adds the metavariable context, the metavariables in existence and their assignments, and with it the operations of type inference (&amp;lt;code&amp;gt;inferType&amp;lt;/code&amp;gt;), reduction (&amp;lt;code&amp;gt;whnf&amp;lt;/code&amp;gt;) and definitional-equality checking (&amp;lt;code&amp;gt;isDefEq&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;TermElabM&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; adds the information the elaborator uses while turning syntax into terms.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; adds the list of current goals.&amp;lt;ref name=&amp;quot;mpb&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 leanprover-community.github.io/lean4-metaprogramming-book/main/04_metam.html]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;CommandElabM&amp;lt;/code&amp;gt;, the monad of commands, stands beside the stack and not on top of&lt;br /&gt;
it; a command that needs to elaborate a term enters &amp;lt;code&amp;gt;TermElabM&amp;lt;/code&amp;gt; with&lt;br /&gt;
&amp;lt;code&amp;gt;liftTermElabM&amp;lt;/code&amp;gt;, as in the second example below.&lt;br /&gt;
&lt;br /&gt;
== Elaborators ==&lt;br /&gt;
&lt;br /&gt;
An &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; gives meaning to a piece of syntax by running Lean code. A term&lt;br /&gt;
elaborator has type &amp;lt;code&amp;gt;Syntax → Option Expr → TermElabM Expr&amp;lt;/code&amp;gt;: it receives the&lt;br /&gt;
syntax and the expected type if one is known, and must return a &amp;lt;code&amp;gt;Lean.Expr&amp;lt;/code&amp;gt;. A&lt;br /&gt;
command elaborator has type &amp;lt;code&amp;gt;Syntax → CommandElabM Unit&amp;lt;/code&amp;gt; and acts by side&lt;br /&gt;
effects, such as adding constants or reporting messages.&amp;lt;ref name=&amp;quot;rm-elab&amp;quot;&amp;gt;&amp;quot;Elaborators&amp;quot;, section 23.6 of &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039; (edition covering Lean 4.34.0). [https://lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Elaborators/ lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Elaborators/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;elab&amp;lt;/code&amp;gt; command declares the syntax and its elaborator together.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont&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;
elab &amp;quot;double! &amp;quot; t:term : term =&amp;gt; do&lt;br /&gt;
  let stx ← `(2 * $t)&lt;br /&gt;
  elabTerm stx (some (mkConst ``Nat))&lt;br /&gt;
&lt;br /&gt;
#eval double! 21&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This prints &amp;lt;code&amp;gt;42&amp;lt;/code&amp;gt;. The elaborator constructs syntax with a quotation,&lt;br /&gt;
&amp;lt;code&amp;gt;`(2 * $t)&amp;lt;/code&amp;gt;, and hands it to &amp;lt;code&amp;gt;elabTerm&amp;lt;/code&amp;gt; with the expected type&lt;br /&gt;
&amp;lt;code&amp;gt;Nat&amp;lt;/code&amp;gt;. Quotations are available in every one of these monads; a quotation of a&lt;br /&gt;
category other than terms names it, as in &amp;lt;code&amp;gt;`(tactic| intro h)&amp;lt;/code&amp;gt;. Their hygiene is&lt;br /&gt;
the hygiene of [[Lean theorem prover/System/Syntax extension and hygienic macros|macros]].&lt;br /&gt;
Unlike a macro, which can do no more with Lean&amp;#039;s environment than check whether a name exists&lt;br /&gt;
and resolve names, an elaborator can consult types, the local context and the whole of Lean&amp;#039;s&lt;br /&gt;
environment.&amp;lt;ref name=&amp;quot;rm-macros&amp;quot;&amp;gt;&amp;quot;Macros&amp;quot;, section 23.5 of &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039; (edition covering Lean 4.34.0), subsection 23.5.2 &amp;quot;The Macro Monad&amp;quot;. [https://lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Macros/ lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Macros/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A command that elaborates a term, puts it in weak head normal form and reports the result:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont&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;
elab &amp;quot;#whnf &amp;quot; t:term : command =&amp;gt; do&lt;br /&gt;
  liftTermElabM do&lt;br /&gt;
    let e ← elabTerm t none&lt;br /&gt;
    let r ← whnf e&lt;br /&gt;
    logInfo m!&amp;quot;{e} reduces to {r} : {← inferType e}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#whnf (fun (x : Nat) =&amp;gt; x + 1) 41&lt;br /&gt;
#whnf (fun (x : Nat) =&amp;gt; [x, x]) 7&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;(fun x =&amp;gt; x + 1) 41 reduces to 42 : Nat&lt;br /&gt;
(fun x =&amp;gt; [x, x]) 7 reduces to [7, 7] : List Nat&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== A tactic ==&lt;br /&gt;
&lt;br /&gt;
A tactic is an elaborator in &amp;lt;code&amp;gt;TacticM&amp;lt;/code&amp;gt; (the&lt;br /&gt;
[[Lean theorem prover/System/The tactic framework|tactic framework]] describes how they are&lt;br /&gt;
run). This one closes the main goal with any hypothesis whose type is definitionally equal&lt;br /&gt;
to it, a simple form of Lean&amp;#039;s &amp;lt;code&amp;gt;assumption&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont&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;
elab &amp;quot;my_assumption&amp;quot; : tactic =&amp;gt; do&lt;br /&gt;
  let goal ← getMainGoal&lt;br /&gt;
  let target ← goal.getType&lt;br /&gt;
  for h in ← getLCtx do&lt;br /&gt;
    if !h.isImplementationDetail &amp;amp;amp;&amp;amp;amp; (← isDefEq h.type target) then&lt;br /&gt;
      goal.assign h.toExpr&lt;br /&gt;
      replaceMainGoal []&lt;br /&gt;
      return&lt;br /&gt;
  throwError &amp;quot;my_assumption: no hypothesis has type {target}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
example (p q : Prop) (hp : p) (hq : q) : p ∧ q :=&lt;br /&gt;
  ⟨by my_assumption, by my_assumption⟩&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each goal is a metavariable, and closing it means assigning it a term: here the free&lt;br /&gt;
variable &amp;lt;code&amp;gt;hp&amp;lt;/code&amp;gt; for the first goal and &amp;lt;code&amp;gt;hq&amp;lt;/code&amp;gt; for the second. When no&lt;br /&gt;
hypothesis fits, the tactic fails with its own message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont 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) : q := by my_assumption&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lean reports &amp;lt;code&amp;gt;my_assumption: no hypothesis has type q&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Metaprograms are not trusted ==&lt;br /&gt;
&lt;br /&gt;
Nothing stops a tactic from assigning a goal a term of the wrong type. The assignment&lt;br /&gt;
operation does not check. This tactic claims to prove anything with &amp;lt;code&amp;gt;True.intro&amp;lt;/code&amp;gt;,&lt;br /&gt;
the proof of &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;lean lean-cont 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;
elab &amp;quot;cheat&amp;quot; : tactic =&amp;gt; do&lt;br /&gt;
  let goal ← getMainGoal&lt;br /&gt;
  goal.assign (mkConst ``True.intro)&lt;br /&gt;
  replaceMainGoal []&lt;br /&gt;
&lt;br /&gt;
theorem bad : False := by cheat&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The tactic runs and reports no failure, but the theorem is refused:&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;(kernel) declaration type mismatch, &amp;#039;bad&amp;#039; has type&lt;br /&gt;
  True&lt;br /&gt;
but it is expected to have type&lt;br /&gt;
  False&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The message is the Lean kernel&amp;#039;s. When a definition or theorem is complete, the elaborator&lt;br /&gt;
passes it to the Lean kernel, which checks it against the rules of the type theory before it&lt;br /&gt;
enters Lean&amp;#039;s environment. The reference manual states the purpose plainly: the elaborator&amp;#039;s&lt;br /&gt;
output must be checked by the Lean kernel &amp;quot;to guard against potential bugs in&lt;br /&gt;
elaboration&amp;quot;.&amp;lt;ref name=&amp;quot;rm-ec&amp;quot;&amp;gt;&amp;quot;Elaboration and Compilation&amp;quot;, a chapter of &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039; (edition covering Lean 4.34.0), section &amp;quot;The Kernel&amp;quot;. [https://lean-lang.org/doc/reference/4.34.0/Elaboration-and-Compilation/ lean-lang.org/doc/reference/4.34.0/Elaboration-and-Compilation/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
The elaborator, the tactic framework, the simplifier and every user&amp;#039;s tactic are outside the&lt;br /&gt;
trusted part of the system. They may be wrong, and, as long as the Lean kernel&amp;#039;s check is not&lt;br /&gt;
switched off, a wrong result costs a failed check, not a false theorem.&lt;br /&gt;
&lt;br /&gt;
This is why metaprogramming can be open to every user. A proof assistant whose automation&lt;br /&gt;
had to be trusted would have to restrict who may write automation, or audit it. Lean instead&lt;br /&gt;
lets any file define tactics and elaborators with the same access its own have, because the&lt;br /&gt;
check at the end does not depend on how the term was found.&lt;br /&gt;
&lt;br /&gt;
The guarantee has limits, and they are worth stating. The check covers only what reaches&lt;br /&gt;
the Lean kernel. The option &amp;lt;code&amp;gt;debug.skipKernelTC&amp;lt;/code&amp;gt; switches it off, and code run during&lt;br /&gt;
elaboration can add a constant to Lean&amp;#039;s environment without passing it to the Lean kernel;&lt;br /&gt;
the reference manual names both as ways of bypassing the check.&amp;lt;ref name=&amp;quot;rm-validating&amp;quot; /&amp;gt; With&lt;br /&gt;
that option set, the &amp;lt;code&amp;gt;cheat&amp;lt;/code&amp;gt; tactic above yields an accepted theorem&lt;br /&gt;
&amp;lt;code&amp;gt;bad : False&amp;lt;/code&amp;gt;. A metaprogram runs with the full rights of input and output, and the&lt;br /&gt;
reference manual&amp;#039;s guidance on validating a proof warns that tactics and other code run at&lt;br /&gt;
elaboration &amp;quot;can perform arbitrary actions when run&amp;quot;, including, in the hostile case,&lt;br /&gt;
interfering with the system before any check takes place.&amp;lt;ref name=&amp;quot;rm-validating&amp;quot;&amp;gt;&amp;quot;Validating a Lean Proof&amp;quot;, a chapter of &amp;#039;&amp;#039;The Lean Language Reference&amp;#039;&amp;#039; (edition covering Lean 4.34.0). [https://lean-lang.org/doc/reference/4.34.0/ValidatingProofs/ lean-lang.org/doc/reference/4.34.0/ValidatingProofs/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
Honest mistakes are caught by the check above. Deliberate tampering is the subject of&lt;br /&gt;
[[Lean theorem prover/System/Proof export and independent checkers|proof export and independent checkers]].&lt;br /&gt;
&lt;br /&gt;
== Next ==&lt;br /&gt;
&lt;br /&gt;
[[Lean theorem prover/System/The compiler and runtime|The compiler and runtime]] explains how&lt;br /&gt;
metaprograms, like all Lean programs, are compiled and run.&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>