doc: add/edit macro guide

Incorporate review changes to the doc book suggested by Kha.
This commit is contained in:
ammkrn 2021-11-20 19:08:12 -06:00 committed by Sebastian Ullrich
parent 7539799976
commit 80c04c562a
3 changed files with 26 additions and 28 deletions

View file

@ -26,9 +26,8 @@
- [The `do` Notation](./do.md)
- [User-defined notation](./notation.md)
- [String Interpolation](./stringinterp.md)
- [Metaprogramming](./metaprogramming.md)
- [Macro Overview](./macro_overview.md)
- [A Guided Example](./metaprogramming_example.md)
- [A Guided Example](./syntax_example.md)
- [Declaring New Types](./decltypes.md)
- [Enumerated Types](./enum.md)
- [Inductive Types](./inductive.md)

View file

@ -33,8 +33,8 @@ itself, and while retaining the simple insert/singleton API.
The general procedure is as follows:
1. Lean parses the actual code in a given file, creating a Lean AST which
contains any unexpanded macros.
1. Lean parses a command, creating a Lean syntax tree which contains any
unexpanded macros.
2. Lean repeats the cycle (elaboration ~> (macro hygiene and expansion) ~>
elaboration...)
@ -45,10 +45,12 @@ macros can expand to other macros, and may expand to code that needs information
from the elaborator. As you can see, the process of macro parsing and expansion
is interleaved with the parsing and elaboration of non-macro code.
By default, macros in Lean are hygienic, but hygiene can be disabled with the
option `set_option hygiene false`. Readers can learn more about hygiene and how
it's implemented in the official paper and supplement linked at the top of this
guide.
By default, macros in Lean are hygienic, which means the system avoids
accidental name capture when reusing the same name inside and outside the macro.
Users may occasionally want to disable hygiene, which can be accomplished with
the command `set_option hygiene false`. More in-depth information about hygiene
and how it's implemented in the official paper and supplement linked at the top
of this guide.
## Elements of "a" macro (important types)
@ -85,10 +87,10 @@ type `Syntax` e.g. `Syntax -> MacroM Syntax`; the leading syntax element is the
thing that actually triggers the macro expansion by matching with the declared
parser, and as a user, you will almost always be interested in inspecting and
transforming that initial syntax element (though there are cases in which it can
just be ignored, as in the exfalso tactic).
just be ignored, as in the parameter-less exfalso tactic).
Returning briefly to the API provided by Lean, `Lean.Syntax`, is pretty much
what you would expect a basic syntax tree type to look like. This is a slightly
what you would expect a basic syntax tree type to look like. Below is a slightly
simplified representation which omits details in the `atom` and `ident`
constructors; users can create atoms and idents which comport with this
simplified representation using the `mkAtom` and `mkIdent` methods provided in
@ -143,8 +145,8 @@ language and `syntax`, `macro_rules`, and `macro` is recommended.
`declare_syntax_cat` declares a new syntax category, like `command`, `tactic`,
or mathlib4's `binderterm`. These are the different categories of things that
can be referred to in a quote/antiquote. Under the hood, `declare_syntax_cat`
just declares a parser descriptor:
can be referred to in a quote/antiquote. `declare_syntax_cat` results in a call
to `registerParserCategory` and produces a new parser descriptor:
```
set_option trace.Elab.definition true in
@ -188,7 +190,6 @@ Lean.ParserDescr.node `tacticRwa__ 1022
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.nonReservedSymbol "rwa " false) Lean.Parser.Tactic.rwRuleSeq)
(Lean.ParserDescr.unary `optional Lean.Parser.Tactic.location))
[Elab.definition.scc] [tacticRwa__]
-/
```
@ -215,8 +216,6 @@ Lean.ParserDescr.node `introv 1022
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.nonReservedSymbol "introv " false)
(Lean.ParserDescr.unary `many
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.const `colGt) (Lean.ParserDescr.const `ident))))
[Elab.definition.scc] [introv]
```
## The pattern language
@ -243,9 +242,9 @@ how it should be displayed, but such whitespace will not prevent a literal with
no trailing whitespace from matching. The spaces are relevant, but not
interpreted literally. When the ParserDescr is turned into a Parser, the actual
token matcher [uses the .trim of the provided
string](https://github.com/leanprover/lean4/blob/master/src/Lean/Parser/Basic.lean#L1193),
string](https://github.com/leanprover/lean4/blob/53ec43ff9b8f55989b12c271e368287b7b997b54/src/Lean/Parser/Basic.lean#L1193),
but the generated formatter [uses the spaces as
specified](https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Formatter.lean#L328),
specified](https://github.com/leanprover/lean4/blob/8d370f151f7c88a687152a5b161dcb484c446ce2/src/Lean/PrettyPrinter/Formatter.lean#L328),
that is, turning the atom "rwa" in the syntax into the string rwa as part of the
pretty printed output.
@ -261,7 +260,7 @@ output the user wants to expand to.
A feature of Lean's macro system is that if there are multiple expansions for a
particular match, Lean will try the most recently declared expansion first, and
will retry with other matching expansions if the previous attempt failed. This
is particularly useful for tactics.
is particularly useful for extending existing tactics.
The following example shows both the retry behavior, and the fact that macros
declared using the shorthand `macro` syntax can still have additional expansions
@ -275,18 +274,18 @@ macro_rules
| `(tactic| transitivity $e) => `(tactic| apply Nat.lt_trans (m := $e))
example (a b c : Nat) (h0 : a < b) (h1 : b < c) : a < c := by
transitivity b <;>
assumption
transitivity b <;>
assumption
example (a b c : Nat) (h0 : a <= b) (h1 : b <= c) : a <= c := by
transitivity b <;>
assumption
transitivity b <;>
assumption
/- This will fail, but is interesting in that it exposes the "most-recent first" behavior, since the
error message complains about being unable to unify mvar1 <= mvar2, rather than mvar1 < mvar2. -/
example (a b c : Nat) (h0 : a <= b) (h1 : b <= c) : False := by
transitivity b <;>
assumption
transitivity b <;>
assumption
```
To see the desugared definition of the actual expansion, we can again use
@ -337,8 +336,8 @@ syntax (name := myExfalsoParser) "myExfalso" : tactic
fun stx => `(tactic| apply False.elim)
example (p : Prop) (h : p) (f : p -> False) : 3 = 2 := by
myExfalso
exact f h
myExfalso
exact f h
```
In the above example, we're still using the sugar Lean provides for creating
@ -352,8 +351,8 @@ syntax (name := myExfalsoParser) "myExfalso" : tactic
#[Lean.mkAtomFrom stx "apply", Lean.mkCIdentFrom stx ``False.elim]
example (p : Prop) (h : p) (f : p -> False) : 3 = 2 := by
myExfalso
exact f h
myExfalso
exact f h
```
## The `macro` keyword