Skip to content
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions doc/style.md
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,87 @@ infix:50 " ⇔ " => Bijection
recommended_spelling "bij" for "⇔" in [Bijection, «term_⇔_»]
```

#### Tactics

Docstrings for tactics should have the following structure:

* Short summary
* Details
* Variants
* Examples

Sometimes more than one declaration is needed to implement what the user
sees as a single tactic. In that case, only one declaration should have
the associated docstring, and the others should have the `tactic_alt`
attribute to mark them as an implementation detail.

The **short summary** should be 1–3 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
docstring is relevant to their task. The first (or only) sentence of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

Suggested change
docstring is relevant to their task. The first (or only) sentence of
tactic is relevant to their task. The first (or only) sentence of

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I adapted this sentence from the section about constants, so I'll make the same change there. (Feel free to revert.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

the short summary should be a full sentence in which the subject
is an example invocation of the tactic, written in present tense
indicative. If the example tactic invocation names parameters, then the
short summary may refer to them. The summary should be written as a
single paragraph.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we do here if the tactic invocation is somewhat syntactically complicated, like match? We should have consistent conventions here. Would we want match e with alts..., for instance, with a second sentence "Each of the alts... has the form | pat (| pat)... => tactics"?

Copy link
Contributor Author

@Vierkantor Vierkantor Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing tactic docs take a lot of different approaches here. For the introduction I'd like to encourage brevity, we don't want to take up all the space with describing the syntax before we can get to the use. So my preference is to take a representative example over completeness: rw [e], not rw [e1, e2, ...]. This is already what we do for cases and induction: "induction x applies induction on x to the main goal, producing one goal for each constructor [...]" and later describing with using an example. (Although I'm not too fond of the docstring, since the bulleted list of syntax forms lists both general explanations and specific examples.)

How does something like this sound?

For the example invocation, prefer the simplest representative example. Explain more complicated forms in the variants section. If needed, abbreviate the invocation by naming part of the syntax and expanding it in the next sentence.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either simplest or most typical (hopefully those coincide, but I think either is fine if they don't).


**Details**, if needed, may be 1-3 paragraphs that describe further
relevant information. They may insert links as needed. This section
should fully explain the scope of the tactic: its syntax format,
on which goals it works and what the resulting goal(s) look like. It
should be clear whether the tactic fails if it does not close the main
goal and if it creates any side goals. The details may include
explanatory examples that can’t necessarily be machine checked and
don’t fit the format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should intentional extension points also fit in here? I'm thinking of tactics that are intended to be extended either by giving them additional macro_rules or by giving some helper tactic the macro_rules. Or should they have a separate paragraph?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, we should mention extensibility in/next to the details section.

For macro_rules based extensible tactics, a paragraph plus example seems the currently standard form, and I think that's sufficient. For example:

You can use the command macro_rules to extend the set of tactics used. Example:

macro_rules | `(tactic| trivial) => `(tactic| simp)

For tactics like ext or grw that use attributes, most explanation should live with the attributes. A single sentence mentioning the attribute here is good though, because it's not obvious that grw looks for @[gcongr] or @[mono] attributes.

Proposal:

If the tactic is extensible using macro_rules, mention this in the
details and give a one-line example. If the tactic provides an attribute
or a command that allows the user to extend its behavior, the
documentation on how to extend the tactic belongs to that attribute or
command. In the tactic docstring, use a single sentence to refer the
reader to this further documentation.

Copy link
Contributor

@robsimmons robsimmons Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This absolutely makes sense to me and I think it's fine as is. However, I would note that as someone who hasn't done as much proving myself, I'm not familiar with macro_rules extension at all and would find your proposed example text a bit confusing! It might be worth describing * Macro Extension as an optional part of the description between * Details and * Variants, and maybe giving (or pointing to) some examples of good tactic documentation that includes macro expansion documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the current text for trivial is not very explanatory. But I don't want tactic docstrings to grow too long either, repeating the same information about macro_rules each time. We already have some explanation in the reference manual. (Once we go to Verso docstrings) we could say here: "Link to the reference manual section on macro extensions."; would that work for you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that's more than sufficient, a hyperlink is what you want, and in lieu of hyperlinks I agree just counting on keyword search is better than repeating the same information over and over.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can link to the manual already. Lean rewrites links of the form lean-manual://section/section-id to the right reference manual for that Lean version. No need to wait for Verso docstrings :)


**Variants**, if needed, should be a bulleted list describing different
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that some tactics may have large variants. How would the docs for induction look in this format? Perhaps the variants should also be able to have names? In Verso, we have a construct like HTML <dl> or LaTeX description that's great for this, and Markdown could have a convention of sticking a title in bold after a bullet.

Perhaps the restriction on having the example as the subject of the sentence can be one option, and a short summary of the syntactic difference be another?

Like:

 * **Named cases***
   A `with` clause results in the cases generated by the tactic being invoked by name, with the tactic ensuring that all cases are handled. `induction e with (pre:tacticSeq)? ((| lbl:ident var:ident*)+ => tacticSeq)+` first runs `pre` in each subgoal, after which the cases labels of all subgoals not closed by `pre` are matched to the provided tactics.

(that text isn't the best, but I think it illustrates a reasonable format that's a bit more accessible than sticking the tactic first).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a big fan of <dl>! How does the following sound:

Each list item should describe an individual variant
and take one of two formats: the short summary as above, or a
named list item. A named list item consists of a title in bold
followed by an indented short paragraph.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great!

options and forms of the same tactic. The reader should be able to parse
and understand the parts of a tactic invocation they are hovering over,
using this list. Each list item should take the format of the **short
summary** above and describe an individual variant.

**Examples** should start with the line `Examples:` (or `Example:` if
there’s exactly one). The section should consist of a sequence of code
blocks, each showing a Lean declaration (usually with the `example`
keyword) that invokes the tactic. When the effect of the tactic is not
clear from the code, you can use code comments to describe this.

##### Example

````
`rw [e]` uses the expression `e` as a rewrite rule on the main goal,
then tries to close the goal by "cheap" (reducible) `rfl`.

If `e` is a defined constant, then the equational theorems associated with `e` are used. This provides a convenient way to unfold `e`.
If `e` has parameters, the tactic will try to fill these in by unification with the matching part of the target.
Parameters are only filled in once per rule, restricting which later rewrites can be found.
Parameters that are not filled in after unification will create side goals.
If the `rfl` fails to close the main goal, no error is raised.

`rw` may fail to rewrite terms "under binders", such as `∀ x, ...` or `∃ x, ...`.
`rw` can also fail with a "motive is type incorrect" error in the context of dependent types.
In these cases, consider using `simp only`.

* `rw [e₁, ... eₙ]` applies the given rules sequentially.
* `rw [← e]` or `rw [<- e]` applies the rewrite in the reverse direction.
* `rw [e] at l` rewrites with `e` at location(s) `l`.
* `rw (occs := .pos L) [e]`, where `L : List Nat`, only rewrites the
given occurrences in the target. Occurrences count from 1.
* `rw (occs := .neg L) [e]`, where `L : List Nat`, skips rewriting the
given occurrences in the target. Occurrences count from 1.

Examples:

```lean
example {a b : Nat} (h : a + a = b) : (a + a) + (a + a) = b + b := by rw [h]
```

```lean
example {f : Nat -> Nat} (h : ∀ x, f x = 1) (a b : Nat) : f a = f b := by
rw [h] -- `rw` instantiates `h` only once, so this is equivalent to: `rw [h a]`
-- goal: ⊢ 1 = f b
rw [h] -- equivalent to: `rw [h b]`
```
````


## Dictionary
Expand Down
Loading