<?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%2FSyntax_extension_and_hygienic_macros</id>
	<title>Lean theorem prover/System/Syntax extension and hygienic macros - 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%2FSyntax_extension_and_hygienic_macros"/>
	<link rel="alternate" type="text/html" href="https://invarians.org/index.php?title=Lean_theorem_prover/System/Syntax_extension_and_hygienic_macros&amp;action=history"/>
	<updated>2026-10-21T07:46:23Z</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/Syntax_extension_and_hygienic_macros&amp;diff=6792&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/Syntax_extension_and_hygienic_macros&amp;diff=6792&amp;oldid=prev"/>
		<updated>2026-10-15T19:35:13Z</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:Syntax extension and hygienic macros}}&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:Syntax extension and hygienic macros}}&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/Programs and proofs in one language}}&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/Programs and proofs in one language}}&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;Lean&amp;#039;s grammar is not fixed. A Lean source file can add operators, notations and whole new&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;Lean&amp;#039;s grammar is not fixed. A Lean source file can add operators, notations and whole new&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key pma_cutover_20261020t051351z-soft_:diff:1.41:old-6585:rev-6792: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/Syntax_extension_and_hygienic_macros&amp;diff=6585&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/Syntax_extension_and_hygienic_macros&amp;diff=6585&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:Syntax extension and hygienic macros}}&lt;br /&gt;
{{Plebeian|Lean theorem prover/Explainers/Programs and proofs in one language}}&lt;br /&gt;
&lt;br /&gt;
Lean&amp;#039;s grammar is not fixed. A Lean source file can add operators, notations and whole new&lt;br /&gt;
syntactic forms, and say what each one means by rewriting it into syntax Lean already&lt;br /&gt;
understands. The rewriting is done by &amp;#039;&amp;#039;&amp;#039;macros&amp;#039;&amp;#039;&amp;#039;, and Lean&amp;#039;s macros are &amp;#039;&amp;#039;&amp;#039;hygienic&amp;#039;&amp;#039;&amp;#039;: a&lt;br /&gt;
name that a macro introduces cannot capture a name the user wrote, and a name the user wrote&lt;br /&gt;
cannot capture one the macro relies on. This page shows the commands, from the simplest&lt;br /&gt;
(&amp;lt;code&amp;gt;infixl&amp;lt;/code&amp;gt;) to the most general (&amp;lt;code&amp;gt;macro_rules&amp;lt;/code&amp;gt;), and what hygiene&lt;br /&gt;
prevents, with examples compiled by Lean 4.34.1. The design is set out by Ullrich and de&lt;br /&gt;
Moura.&amp;lt;ref name=&amp;quot;bn&amp;quot;&amp;gt;Sebastian Ullrich and Leonardo de Moura, &amp;quot;Beyond Notations: Hygienic Macro Expansion for Theorem Proving Languages&amp;quot;, &amp;#039;&amp;#039;Logical Methods in Computer Science&amp;#039;&amp;#039; 18(2), 2022, article 1, 21 pp. Extended version of the paper in &amp;#039;&amp;#039;Automated Reasoning: IJCAR 2020&amp;#039;&amp;#039;, Lecture Notes in Computer Science 12167, Springer, 2020, pp. 167–182 (doi:10.1007/978-3-030-51054-1_10). doi:[https://doi.org/10.46298/lmcs-18(2:1)2022 10.46298/lmcs-18(2:1)2022]; preprint [https://arxiv.org/abs/2001.10490 arXiv:2001.10490].&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Where syntax extension sits ==&lt;br /&gt;
&lt;br /&gt;
Lean reads a source file one command at a time. Each command is parsed, then&lt;br /&gt;
[[Lean theorem prover/System/Elaboration|elaborated]] into terms of the type theory, and only&lt;br /&gt;
the elaborated result is passed to the Lean kernel. Macro expansion happens after parsing, as&lt;br /&gt;
part of elaboration: before a piece of syntax is elaborated, any macro at its outermost layer&lt;br /&gt;
is expanded.&amp;lt;ref name=&amp;quot;rm-macros&amp;quot; /&amp;gt; A macro takes a piece of syntax and returns another piece of syntax; the&lt;br /&gt;
reference manual calls the expansion of a notation &amp;quot;a purely syntactic process that occurs&lt;br /&gt;
prior to elaboration or code generation&amp;quot;.&amp;lt;ref name=&amp;quot;rm-notations&amp;quot;&amp;gt;&amp;quot;Notations&amp;quot;, section 23.3 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/Notations/ lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Notations/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
Whatever a macro produces is then elaborated and checked exactly as if the user had typed&lt;br /&gt;
it. A macro can change what is written; it cannot prove anything that the text it produces&lt;br /&gt;
could not prove if typed by hand.&lt;br /&gt;
&lt;br /&gt;
The commands form a tower. The operator commands are shorthands for &amp;lt;code&amp;gt;notation&amp;lt;/code&amp;gt;.&lt;br /&gt;
In Ullrich and de Moura&amp;#039;s account, &amp;lt;code&amp;gt;notation&amp;lt;/code&amp;gt; is itself a macro that generates a&lt;br /&gt;
&amp;lt;code&amp;gt;macro&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;macro&amp;lt;/code&amp;gt; expands into a grammar rule (&amp;lt;code&amp;gt;syntax&amp;lt;/code&amp;gt;)&lt;br /&gt;
paired with a rewriting rule (&amp;lt;code&amp;gt;macro_rules&amp;lt;/code&amp;gt;).&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt;&amp;lt;ref name=&amp;quot;rm-ops&amp;quot; /&amp;gt;&lt;br /&gt;
In Lean 4.34.1, &amp;lt;code&amp;gt;notation&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;macro&amp;lt;/code&amp;gt; are each handled by an&lt;br /&gt;
elaborator that produces this pair directly.&amp;lt;ref name=&amp;quot;src-notation&amp;quot;&amp;gt;Lean 4 source code, version 4.34.1 (2026), files &amp;lt;code&amp;gt;src/Lean/Elab/Notation.lean&amp;lt;/code&amp;gt; (the functions &amp;lt;code&amp;gt;elabNotation&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;mkUnexpander&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;src/Lean/Elab/Macro.lean&amp;lt;/code&amp;gt; (the function &amp;lt;code&amp;gt;elabMacro&amp;lt;/code&amp;gt;). Apache License 2.0. https://github.com/leanprover/lean4/blob/v4.34.1/src/Lean/Elab/Notation.lean&amp;lt;/ref&amp;gt; The&lt;br /&gt;
&amp;lt;code&amp;gt;syntax&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;macro_rules&amp;lt;/code&amp;gt; commands in turn become ordinary Lean&lt;br /&gt;
definitions that the parser and the macro expander consult.&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Operators ==&lt;br /&gt;
&lt;br /&gt;
The smallest extension names a binary operator.&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;
def avg (a b : Nat) : Nat := (a + b) / 2&lt;br /&gt;
&lt;br /&gt;
infixl:65 &amp;quot; ⊞ &amp;quot; =&amp;gt; avg&lt;br /&gt;
&lt;br /&gt;
#eval 2 ⊞ 4 ⊞ 10&lt;br /&gt;
#check fun (a b : Nat) =&amp;gt; avg a b&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;#eval&amp;lt;/code&amp;gt; prints &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: the operator associates to the left, so the&lt;br /&gt;
expression is &amp;lt;code&amp;gt;avg (avg 2 4) 10&amp;lt;/code&amp;gt;, that is &amp;lt;code&amp;gt;avg 3 10&amp;lt;/code&amp;gt;. The reference&lt;br /&gt;
manual gives the expansion exactly: &amp;lt;code&amp;gt;infixl:prec &amp;quot;op&amp;quot; =&amp;gt; f&amp;lt;/code&amp;gt; stands for&lt;br /&gt;
&amp;lt;code&amp;gt;notation:prec x:prec &amp;quot;op&amp;quot; y:prec1 =&amp;gt; f x y&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;prec1&amp;lt;/code&amp;gt; is one more&lt;br /&gt;
than &amp;lt;code&amp;gt;prec&amp;lt;/code&amp;gt;, so the left operand may itself be a use of the operator and the right&lt;br /&gt;
operand may not. A custom operator must state its precedence; there is no default.&amp;lt;ref name=&amp;quot;rm-ops&amp;quot;&amp;gt;&amp;quot;Custom Operators&amp;quot;, section 23.1 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/Custom-Operators/ lean-lang.org/doc/reference/4.34.0/Notations-and-Macros/Custom-Operators/]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;#check&amp;lt;/code&amp;gt; shows the other direction. Asked to print &amp;lt;code&amp;gt;avg a b&amp;lt;/code&amp;gt;, Lean&lt;br /&gt;
prints&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 a b =&amp;gt; a ⊞ b : Nat → Nat → Nat&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command has also generated an &amp;#039;&amp;#039;&amp;#039;unexpander&amp;#039;&amp;#039;&amp;#039;, a rule used when terms are displayed. A&lt;br /&gt;
notation gets one when its right-hand side applies a named function and each of its term&lt;br /&gt;
positions occurs there exactly once; a notation that expands to an anonymous function gets&lt;br /&gt;
none.&amp;lt;ref name=&amp;quot;rm-notations&amp;quot; /&amp;gt;&amp;lt;ref name=&amp;quot;rm-ops&amp;quot; /&amp;gt;&lt;br /&gt;
The operator therefore appears in goals and error messages, not only in source text.&lt;br /&gt;
&lt;br /&gt;
== Notation, syntax and macro rules ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;notation&amp;lt;/code&amp;gt; accepts any mixture of tokens and term positions, and its right-hand&lt;br /&gt;
side is a template. When a template is not enough, the two halves can be written separately.&lt;br /&gt;
&amp;lt;code&amp;gt;syntax&amp;lt;/code&amp;gt; adds a grammar rule to a &amp;#039;&amp;#039;&amp;#039;syntax category&amp;#039;&amp;#039;&amp;#039; (here &amp;lt;code&amp;gt;term&amp;lt;/code&amp;gt;),&lt;br /&gt;
and &amp;lt;code&amp;gt;macro_rules&amp;lt;/code&amp;gt; says how to rewrite 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;
syntax &amp;quot;∑ &amp;quot; ident &amp;quot; ∈ &amp;quot; term &amp;quot;, &amp;quot; term : term&lt;br /&gt;
&lt;br /&gt;
macro_rules&lt;br /&gt;
  | `(∑ $i ∈ $xs, $body) =&amp;gt; `(List.foldl (fun acc $i =&amp;gt; acc + $body) 0 $xs)&lt;br /&gt;
&lt;br /&gt;
#eval ∑ i ∈ [1, 2, 3, 4], i * i&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The output is &amp;lt;code&amp;gt;30&amp;lt;/code&amp;gt;, the sum of the squares of 1 to 4. The pattern on the left of&lt;br /&gt;
&amp;lt;code&amp;gt;=&amp;gt;&amp;lt;/code&amp;gt; and the result on the right are &amp;#039;&amp;#039;&amp;#039;syntax quotations&amp;#039;&amp;#039;&amp;#039;, written&lt;br /&gt;
&amp;lt;code&amp;gt;`(...)&amp;lt;/code&amp;gt;. Inside them &amp;lt;code&amp;gt;$i&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$xs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;$body&amp;lt;/code&amp;gt; are&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;antiquotations&amp;#039;&amp;#039;&amp;#039;: holes that match syntax in a pattern and insert it in a result. A&lt;br /&gt;
quotation produces syntax, not a term; it is parsed but not elaborated.&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), including 23.5.1 &amp;quot;Hygiene&amp;quot;, 23.5.3 &amp;quot;Quotation&amp;quot; and 23.5.5 &amp;quot;Defining Macros&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;
&amp;lt;code&amp;gt;macro_rules&amp;lt;/code&amp;gt; is open. More rules for the same syntax may be added later, in the&lt;br /&gt;
same file or another, and the most recently defined is tried first. A rule that does not&lt;br /&gt;
apply hands the syntax on to the next one, by throwing the exception&lt;br /&gt;
&amp;lt;code&amp;gt;unsupportedSyntax&amp;lt;/code&amp;gt;; any other exception is reported as an error. Only the&lt;br /&gt;
outermost layer of syntax is expanded at a time, and nested macros are expanded when the&lt;br /&gt;
elaborator reaches them.&amp;lt;ref name=&amp;quot;rm-macros&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Syntax categories are not limited to terms. Commands and tactics are categories too, and a&lt;br /&gt;
library may declare its own with &amp;lt;code&amp;gt;declare_syntax_cat&amp;lt;/code&amp;gt;. Ullrich and de Moura give an&lt;br /&gt;
example of why this matters: a big-operator file in Coq&amp;#039;s Mathematical Components library&lt;br /&gt;
repeats twelve notations for index ranges once for each of three operators, because Coq&amp;#039;s&lt;br /&gt;
notation system cannot abstract over the index syntax; with a category for index ranges,&lt;br /&gt;
each range form is interpreted once and every operator reuses it.&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What hygiene prevents ==&lt;br /&gt;
&lt;br /&gt;
A macro is a function on syntax, and syntax contains names. Two kinds of accident follow&lt;br /&gt;
from that.&lt;br /&gt;
&lt;br /&gt;
The first is &amp;#039;&amp;#039;&amp;#039;variable capture&amp;#039;&amp;#039;&amp;#039;: a binder the macro introduces captures a name the user&lt;br /&gt;
supplied. Ullrich and de Moura&amp;#039;s example is a notation for constant&lt;br /&gt;
functions.&amp;lt;ref name=&amp;quot;bn&amp;quot; /&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;
notation &amp;quot;const&amp;quot; e =&amp;gt; fun x =&amp;gt; e&lt;br /&gt;
&lt;br /&gt;
#check fun (x : Nat) =&amp;gt; const x&lt;br /&gt;
#eval (fun (x : Nat) =&amp;gt; const x) 1 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Read as text substitution, &amp;lt;code&amp;gt;const x&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;fun x =&amp;gt; x&amp;lt;/code&amp;gt;, the&lt;br /&gt;
identity, and the whole expression would return its second argument. Lean prints&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 x_1 =&amp;gt; x : (x : Nat) → ?m.2 x → Nat&lt;br /&gt;
1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The binder that the notation introduced has been renamed (Lean displays it as&lt;br /&gt;
&amp;lt;code&amp;gt;x_1&amp;lt;/code&amp;gt;), the user&amp;#039;s &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; still refers to the outer binder, and the&lt;br /&gt;
function returns its first argument. The mechanism is the &amp;#039;&amp;#039;&amp;#039;macro scope&amp;#039;&amp;#039;&amp;#039;. Each expansion of&lt;br /&gt;
a macro tags every identifier it introduces with a fresh scope, and a binder and a use of a&lt;br /&gt;
name match only if their tags agree.&amp;lt;ref name=&amp;quot;rm-macros&amp;quot; /&amp;gt;&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt; The user&amp;#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; carries no tag and the macro&amp;#039;s carries one, so they are different names.&lt;br /&gt;
&lt;br /&gt;
Hygiene can be switched off, and the same notation then captures.&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 hygiene false in&lt;br /&gt;
notation &amp;quot;const&amp;#039;&amp;quot; e =&amp;gt; fun x =&amp;gt; e&lt;br /&gt;
&lt;br /&gt;
#eval (fun (x : Nat) =&amp;gt; const&amp;#039; x) 1 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This prints &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;, and Lean&amp;#039;s linter warns that the outer &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is never&lt;br /&gt;
referenced: the macro&amp;#039;s binder has taken it. The summation notation above is protected in&lt;br /&gt;
the same way. Its accumulator is called &amp;lt;code&amp;gt;acc&amp;lt;/code&amp;gt;, and a user&amp;#039;s variable of that name&lt;br /&gt;
is not affected.&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;
#eval let acc := 100; ∑ i ∈ [1, 2, 3], acc * i&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result is &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt;. Rewritten by plain substitution, the body&lt;br /&gt;
&amp;lt;code&amp;gt;acc * i&amp;lt;/code&amp;gt; would refer to the accumulator, which starts at 0, and the result would&lt;br /&gt;
be 0.&lt;br /&gt;
&lt;br /&gt;
The second kind of accident runs the other way: a local name at the use site captures a&lt;br /&gt;
global name the macro relies on.&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;
def double (n : Nat) : Nat := 2 * n&lt;br /&gt;
&lt;br /&gt;
macro &amp;quot;dbl! &amp;quot; e:term : term =&amp;gt; `(double $e)&lt;br /&gt;
&lt;br /&gt;
#eval let double := fun (n : Nat) =&amp;gt; n; dbl! 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The output is &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;, and Lean warns that the local variable &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt; is&lt;br /&gt;
not referenced. The identifier &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt; in the quotation carries the macro&amp;#039;s scope&lt;br /&gt;
and the local binding at the use site does not, so the two do not match. When the quotation&lt;br /&gt;
&amp;lt;code&amp;gt;`(double $e)&amp;lt;/code&amp;gt; was compiled, the name was also resolved to the global function and&lt;br /&gt;
recorded with it, so a global of the same name introduced later cannot redirect it&lt;br /&gt;
either.&amp;lt;ref name=&amp;quot;rm-macros&amp;quot; /&amp;gt;&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hygiene in tactics ==&lt;br /&gt;
&lt;br /&gt;
Tactics form a syntax category, so a tactic can be a macro (the&lt;br /&gt;
[[Lean theorem prover/System/The tactic framework|tactic framework]] treats the result like&lt;br /&gt;
any other tactic). This one introduces a hypothesis and uses 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;
macro &amp;quot;intro_assumption&amp;quot; : tactic =&amp;gt; `(tactic| (intro h; exact h))&lt;br /&gt;
&lt;br /&gt;
example (p : Prop) : p → p := by intro_assumption&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proof is accepted. The name &amp;lt;code&amp;gt;h&amp;lt;/code&amp;gt;, however, belongs to the macro. A script that&lt;br /&gt;
uses a macro to introduce &amp;lt;code&amp;gt;h&amp;lt;/code&amp;gt; and then refers to &amp;lt;code&amp;gt;h&amp;lt;/code&amp;gt; itself 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;
macro &amp;quot;intro_hyp&amp;quot; : tactic =&amp;gt; `(tactic| intro h)&lt;br /&gt;
&lt;br /&gt;
example (p : Prop) : p → p := by&lt;br /&gt;
  intro_hyp&lt;br /&gt;
  exact h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lean reports &amp;lt;code&amp;gt;Unknown identifier `h`&amp;lt;/code&amp;gt; at the last line. The hypothesis exists,&lt;br /&gt;
but under a name the user cannot type:&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;intro_hyp&amp;quot; : tactic =&amp;gt; `(tactic| intro h)&lt;br /&gt;
&lt;br /&gt;
example (p : Prop) : p → p := by&lt;br /&gt;
  intro_hyp&lt;br /&gt;
  trace_state&lt;br /&gt;
  assumption&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;p : Prop&lt;br /&gt;
h✝ : p&lt;br /&gt;
⊢ p&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The dagger marks an inaccessible name, and &amp;lt;code&amp;gt;assumption&amp;lt;/code&amp;gt;, which does not need the&lt;br /&gt;
name, closes the goal. Ullrich and de Moura report that Lean 3&amp;#039;s tactic quotations had both&lt;br /&gt;
faults: a short tactic script for injectivity lemmas broke when it was used from a library&lt;br /&gt;
for homotopy type theory that defined its own &amp;lt;code&amp;gt;propext&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;Iff.intro&amp;lt;/code&amp;gt;, and scripts could refer to names a tactic had introduced. They&lt;br /&gt;
describe Lean 4&amp;#039;s tactic language as, to their knowledge, the first in an established theorem&lt;br /&gt;
prover to be hygienic automatically in this sense.&amp;lt;ref name=&amp;quot;bn&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mathematics in its own notation ==&lt;br /&gt;
&lt;br /&gt;
Mathematics is written in notation that varies by field and by author, and much of it binds&lt;br /&gt;
variables: sums, products, integrals, set-builder notation, quantifiers over a finite set. A&lt;br /&gt;
fixed grammar forces a formal text away from that notation. Lean&amp;#039;s macros let a library add&lt;br /&gt;
it, with three properties that matter in practice.&lt;br /&gt;
&lt;br /&gt;
Binding notation is safe to define. A notation that introduces its own bound variables, such&lt;br /&gt;
as the sum above, does not interfere with the names its user chooses, and the user need not&lt;br /&gt;
know which names it uses inside. Without hygiene, every such notation would be a hazard of&lt;br /&gt;
the kind shown in the &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt; example.&lt;br /&gt;
&lt;br /&gt;
Notation is printed back. Because an unexpander is generated where possible, goals and error&lt;br /&gt;
messages show the notation the reader wrote, not the function it stands for.&lt;br /&gt;
&lt;br /&gt;
Notation can be confined. A notation marked &amp;lt;code&amp;gt;scoped&amp;lt;/code&amp;gt; is active only where its&lt;br /&gt;
namespace is open, so a field&amp;#039;s notation need not occupy the grammar of every other field.&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;
def avg (a b : Nat) : Nat := (a + b) / 2&lt;br /&gt;
&lt;br /&gt;
namespace Averages&lt;br /&gt;
scoped infixl:65 &amp;quot; ⊞ &amp;quot; =&amp;gt; avg&lt;br /&gt;
end Averages&lt;br /&gt;
&lt;br /&gt;
open Averages in&lt;br /&gt;
#eval 2 ⊞ 4 ⊞ 10&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This prints &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;. Without the &amp;lt;code&amp;gt;open&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;⊞&amp;lt;/code&amp;gt; is not part of the&lt;br /&gt;
grammar:&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;
def avg (a b : Nat) : Nat := (a + b) / 2&lt;br /&gt;
&lt;br /&gt;
namespace Averages&lt;br /&gt;
scoped infixl:65 &amp;quot; ⊞ &amp;quot; =&amp;gt; avg&lt;br /&gt;
end Averages&lt;br /&gt;
&lt;br /&gt;
#check (2 ⊞ 4 : Nat)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lean stops at the operator with the parse error &amp;lt;code&amp;gt;expected token&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
What notation does not do is add meaning. It changes how a statement is written and read,&lt;br /&gt;
and nothing that the Lean kernel checks. That is also its risk. A statement that uses&lt;br /&gt;
unfamiliar notation may not say what it appears to say, and the reference manual&amp;#039;s guidance&lt;br /&gt;
on validating a proof names custom notation among the things to investigate when there is&lt;br /&gt;
doubt that a theorem states what was intended.&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), section &amp;quot;Remaining Issues&amp;quot;. [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;
&lt;br /&gt;
A macro sees only syntax. When a translation depends on types, for example on whether an&lt;br /&gt;
argument is a natural number or a set, a macro cannot decide it, and an &amp;#039;&amp;#039;&amp;#039;elaborator&amp;#039;&amp;#039;&amp;#039; is&lt;br /&gt;
needed: a Lean program that runs inside the elaborator and constructs terms directly.&lt;br /&gt;
&lt;br /&gt;
== Next ==&lt;br /&gt;
&lt;br /&gt;
[[Lean theorem prover/System/Metaprogramming in Lean|Metaprogramming in Lean]] shows how&lt;br /&gt;
elaborators and tactics are written as Lean programs.&lt;br /&gt;
[[Lean theorem prover/System/Monads and do-notation|Monads and do-notation]] describes a&lt;br /&gt;
piece of syntax that Lean translates into ordinary terms: the &amp;lt;code&amp;gt;do&amp;lt;/code&amp;gt; block.&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>