Documentation commands #
We generate html documentation from mathlib. It is convenient to collect lists of tactics, commands, notes, etc. To facilitate this, we declare these documentation entries in the library using special commands.
library_noteadds a note describing a certain feature or design decision. These can be referenced in doc strings with the textnote [name of note].add_tactic_docadds an entry documenting an interactive tactic, command, hole command, or attribute.
Since these commands are used in files imported by tactic.core, this file has no imports.
Implementation details #
library_note note_id note_msg creates a declaration `library_note.i for some i.
This declaration is a pair of strings note_id and note_msg, and it gets tagged with the
library_note attribute.
Similarly, add_tactic_doc creates a declaration `tactic_doc.i that stores the provided
information.
A rudimentary hash function on strings.
Equations
- s.hash = string.fold 1 (λ (h : ℕ) (c : char), (33 * h + c.val) % unsigned_sz) s
The library_note command #
The add_tactic_doc_entry command #
- tactic : doc_category
- cmd : doc_category
- hole_cmd : doc_category
- attr : doc_category
The categories of tactic doc entry.
Instances for doc_category
- doc_category.has_sizeof_inst
- doc_category.has_reflect
- doc_category.has_to_format
- doc_category.inhabited
- name : string
- category : doc_category
- decl_names : list name
- tags : list string
- inherit_description_from : option name
The information used to generate a tactic doc entry
Instances for tactic_doc_entry
- tactic_doc_entry.has_sizeof_inst
- tactic_doc_entry.has_reflect
- tactic_doc_entry.inhabited
At various places in mathlib, we leave implementation notes that are referenced from many other
files. To keep track of these notes, we use the command library_note. This makes it easy to
retrieve a list of all notes, e.g. for documentation output.
These notes can be referenced in mathlib with the syntax Note [note id].
Often, these references will be made in code comments (--) that won't be displayed in docs.
If such a reference is made in a doc string or module doc, it will be linked to the corresponding
note in the doc display.
Syntax:
/--
note message
-/
library_note "note id"
An example from meta.expr:
/--
Some declarations work with open expressions, i.e. an expr that has free variables.
Terms will free variables are not well-typed, and one should not use them in tactics like
`infer_type` or `unify`. You can still do syntactic analysis/manipulation on them.
The reason for working with open types is for performance: instantiating variables requires
iterating through the expression. In one performance test `pi_binders` was more than 6x
quicker than `mk_local_pis` (when applied to the type of all imported declarations 100x).
-/
library_note "open expressions"
This note can be referenced near a usage of pi_binders:
-- See Note [open expressions]
/-- behavior of f -/
def f := pi_binders ...
A command used to add documentation for a tactic, command, hole command, or attribute.
Usage: after defining an interactive tactic, command, or attribute, add its documentation as follows.
/--
describe what the command does here
-/
add_tactic_doc
{ name := "display name of the tactic",
category := cat,
decl_names := [`dcl_1, `dcl_2],
tags := ["tag_1", "tag_2"] }
The argument to add_tactic_doc is a structure of type tactic_doc_entry.
namerefers to the display name of the tactic; it is used as the header of the doc entry.catrefers to the category of doc entry. Options:doc_category.tactic,doc_category.cmd,doc_category.hole_cmd,doc_category.attrdecl_namesis a list of the declarations associated with this doc. For instance, the entry forlinarithwould setdecl_names := [`tactic.interactive.linarith]. Some entries may cover multiple declarations. It is only necessary to list the interactive versions of tactics.tagsis an optional list of strings used to categorize entries.- The doc string is the body of the entry. It can be formatted with markdown.
What you are reading now is the description of
add_tactic_doc.
If only one related declaration is listed in decl_names and if this
invocation of add_tactic_doc does not have a doc string, the doc string of
that declaration will become the body of the tactic doc entry. If there are
multiple declarations, you can select the one to be used by passing a name to
the inherit_description_from field.
If you prefer a tactic to have a doc string that is different then the doc entry,
you should write the doc entry as a doc string for the add_tactic_doc invocation.
Note that providing a badly formed tactic_doc_entry to the command can result in strange error
messages.
copy_doc_string source → target_1 target_2 ... target_n copies the doc string of the
declaration named source to each of target_1, target_2, ..., target_n.
The congruence closure tactic cc tries to solve the goal by chaining
equalities from context and applying congruence (i.e. if a = b, then f a = f b).
It is a finishing tactic, i.e. it is meant to close
the current goal, not to make some inconclusive progress.
A mostly trivial example would be:
example (a b c : ℕ) (f : ℕ → ℕ) (h: a = b) (h' : b = c) : f a = f c := by cc
As an example requiring some thinking to do by hand, consider:
example (f : ℕ → ℕ) (x : ℕ)
(H1 : f (f (f x)) = x) (H2 : f (f (f (f (f x)))) = x) :
f x = x :=
by cc
The tactic works by building an equality matching graph. It's a graph where the vertices are terms and they are linked by edges if they are known to be equal. Once you've added all the equalities in your context, you take the transitive closure of the graph and, for each connected component (i.e. equivalence class) you can elect a term that will represent the whole class and store proofs that the other elements are equal to it. You then take the transitive closure of these equalities under the congruence lemmas.
The cc implementation in Lean does a few more tricks: for example it
derives a=b from nat.succ a = nat.succ b, and nat.succ a != nat.zero for any a.
-
The starting reference point is Nelson, Oppen, Fast decision procedures based on congruence closure, Journal of the ACM (1980)
-
The congruence lemmas for dependent type theory as used in Lean are described in Congruence closure in intensional type theory (de Moura, Selsam IJCAR 2016).
conv {...} allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.
See https://leanprover-community.github.io/extras/conv.html for more details.
Inside conv blocks, mathlib currently additionally provides
apply_congr applies congruence lemmas to step further inside expressions,
and sometimes gives better results than the automatically generated
congruence lemmas used by congr.
Using conv inside a conv block allows the user to return to the previous
state of the outer conv block after it is finished. Thus you can continue
editing an expression without having to start a new conv block and re-scoping
everything. For example:
example (a b c d : ℕ) (h₁ : b = c) (h₂ : a + c = a + d) : a + b = a + d :=
by conv
{ to_lhs,
conv
{ congr, skip,
rw h₁ },
rw h₂, }
Without conv, the above example would need to be proved using two successive
conv blocks, each beginning with to_lhs.
Also, as a shorthand, conv_lhs and conv_rhs are provided, so that
example : 0 + 0 = 0 :=
begin
conv_lhs { simp }
end
just means
example : 0 + 0 = 0 :=
begin
conv { to_lhs, simp }
end
and likewise for to_rhs.
The simp tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants.
simp simplifies the main goal target using lemmas tagged with the attribute [simp].
simp [h₁ h₂ ... hₙ] simplifies the main goal target using the lemmas tagged with the attribute [simp] and the given hᵢ's, where the hᵢ's are expressions. If hᵢ is preceded by left arrow (← or <-), the simplification is performed in the reverse direction. If an hᵢ is a defined constant f, then the equational lemmas associated with f are used. This provides a convenient way to unfold f.
simp [*] simplifies the main goal target using the lemmas tagged with the attribute [simp] and all hypotheses.
simp * is a shorthand for simp [*].
simp only [h₁ h₂ ... hₙ] is like simp [h₁ h₂ ... hₙ] but does not use [simp] lemmas
simp [-id_1, ... -id_n] simplifies the main goal target using the lemmas tagged with the attribute [simp], but removes the ones named idᵢ.
simp at h₁ h₂ ... hₙ simplifies the non-dependent hypotheses h₁ : T₁ ... hₙ : Tₙ. The tactic fails if the target or another hypothesis depends on one of them. The token ⊢ or |- can be added to the list to include the target.
simp at * simplifies all the hypotheses and the target.
simp * at * simplifies target and all (non-dependent propositional) hypotheses using the other hypotheses.
simp with attr₁ ... attrₙ simplifies the main goal target using the lemmas tagged with any of the attributes [attr₁], ..., [attrₙ] or [simp].
Accepts terms with the type component tactic_state string or html empty and
renders them interactively.
Requires a compatible version of the vscode extension to view the resulting widget.
Example: #
/-- A simple counter that can be incremented or decremented with some buttons. -/
meta def counter_widget {π α : Type} : component π α :=
component.ignore_props $ component.mk_simple int int 0 (λ _ x y, (x + y, none)) (λ _ s,
h "div" [] [
button "+" (1 : int),
html.of_string $ to_string $ s,
button "-" (-1)
]
)
#html counter_widget
```
The add_decl_doc command is used to add a doc string to an existing declaration.
def foo := 5
/--
Doc string for foo.
-/
add_decl_doc foo
```