Skip to content

Commit 0285ced

Browse files
authored
fix: repair many examples in manual to run in isolation (#617)
There are a few dozen `:::example` directives in the manual that won't run correctly if you copy and paste them into https://live.lean-lang.org/ . Fix them all, and include a CI check to ensure that they are all fixes. A sampling of reasons include: - `#check_decl` is defined in reference manual but not available in live - `import`s of `Lean` or `Std` are missing - `open`s prior to the `:::example` are necessary - important definitions are in `+error` blocks and needed to be hoisted . Progress towards leanprover/verso#569 .
1 parent e3df4b3 commit 0285ced

File tree

23 files changed

+222
-42
lines changed

23 files changed

+222
-42
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ jobs:
131131
rm parse_times.py
132132
else
133133
echo "No build log found" >> $GITHUB_STEP_SUMMARY
134-
fi
134+
fi
135135
136136
- name: Save cache for .lake
137137
uses: actions/cache/save@v4
@@ -149,6 +149,10 @@ jobs:
149149
run: |
150150
lake --quiet exe generate-manual --depth 2 --with-word-count "words.txt" --verbose --without-html-single
151151
152+
- name: Check Manual Examples are Isolated
153+
run: |
154+
scripts/check-examples-isolated.sh
155+
152156
- name: Generate proofreading HTML
153157
if: github.event_name == 'pull_request'
154158
id: generate
@@ -380,4 +384,3 @@ jobs:
380384
run: |
381385
cd _out/html-multi/-verso-search
382386
tsc --noEmit -p jsconfig.json
383-

Manual/Axioms.lean

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ Consider the following three constants:
272272

273273
```lean
274274
def addThree (n : Nat) : Nat := 1 + n + 2
275-
theorem excludedMiddle (P : Prop) : P ∨ ¬ P := Classical.em P
276-
theorem simpleEquality (P : Prop) : (P ∨ False) = P := or_false P
275+
theorem excluded_middle (P : Prop) : P ∨ ¬ P := Classical.em P
276+
theorem simple_equality (P : Prop) : (P ∨ False) = P := or_false P
277277
```
278278

279279
Regular functions like {lean}`addThree` that we might want to actually evaluation typically do not depend on any axioms:
@@ -288,35 +288,40 @@ Regular functions like {lean}`addThree` that we might want to actually evaluatio
288288
The excluded middle theorem is only true if we use classical reasoning, so the foundation for classical reasoning shows up alongside other axioms:
289289

290290
```lean (name := printAxEx1)
291-
#print axioms excludedMiddle
291+
#print axioms excluded_middle
292292
```
293293
```leanOutput printAxEx1
294-
'excludedMiddle' depends on axioms: [propext, Classical.choice, Quot.sound]
294+
'excluded_middle' depends on axioms: [propext, Classical.choice, Quot.sound]
295295
```
296296

297297
Finally, the idea that two equivalent propositions are equal directly relies on {tech}[propositional extensionality].
298298

299299
```lean (name := printAxEx3)
300-
#print axioms simpleEquality
300+
#print axioms simple_equality
301301
```
302302
```leanOutput printAxEx3
303-
'simpleEquality' depends on axioms: [propext]
303+
'simple_equality' depends on axioms: [propext]
304304
```
305305
:::
306306

307307
:::example "Using {keywordOf Lean.Parser.Command.printAxioms}`#print axioms` with {keywordOf Lean.guardMsgsCmd}`#guard_msgs`"
308308

309-
You can use {keywordOf Lean.Parser.Command.printAxioms}`#print axioms` together with {keywordOf Lean.guardMsgsCmd}`#guard_msgs` to ensure that updates to libraries from other projects cannot silently introduce unwanted dependencies on axioms.
309+
You can use {keywordOf Lean.Parser.Command.printAxioms}`#print axioms`
310+
together with {keywordOf Lean.guardMsgsCmd}`#guard_msgs` to ensure
311+
that updates to libraries from other projects cannot silently
312+
introduce unwanted dependencies on axioms.
310313

311-
Perhaps you are worried that some future update to {lean}`or_false` in the previous example's {lean}`simpleEquality` proof might quietly introduce a new axiom dependency.
312-
You can guard against this by writing a command that will fail if {lean}`simpleEquality` uses any axioms besides {lean}`propext`:
314+
For example, if the proof of {name}`double_neg_elim` below changed in such a way that it used more
315+
axioms than those listed, then the {keywordOf Lean.guardMsgsCmd}`#guard_msgs` would report an error.
313316

314317
```lean
315-
/--
316-
info: 'simpleEquality' depends on axioms: [propext]
317-
-/
318+
theorem double_neg_elim (P : Prop) : (¬ ¬ P) = P :=
319+
propext Classical.not_not
320+
321+
/-- info: 'double_neg_elim' depends on axioms: [propext, Classical.choice, Quot.sound] -/
318322
#guard_msgs in
319-
#print axioms simpleEquality
323+
#print axioms double_neg_elim
324+
320325
```
321326
:::
322327

Manual/BasicTypes/BitVec.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ The resulting proofs rely only on the axiom {name}`Lean.ofReduceBool`; the exter
170170

171171
:::example "Popcount"
172172

173+
```imports
174+
import Std.Tactic.BVDecide
175+
```
176+
173177
The function {lean}`popcount` returns the number of set bits in a bitvector.
174178
It can be implemented as a 32-iteration loop that tests each bit, incrementing a counter if the bit is set:
175179

Manual/BasicTypes/Maps.lean

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Author: David Thrane Christiansen
77
import VersoManual
88

99
import Manual.Meta
10+
import Manual.Meta.ImportsBlock
1011

1112
import Manual.BasicTypes.Maps.TreeSet
1213
import Manual.BasicTypes.Maps.TreeMap
@@ -176,6 +177,10 @@ A nested inductive type that occurs inside a map or set should be defined in thr
176177

177178
:::example "Nested Inductive Types with `Std.HashMap`"
178179

180+
```imports
181+
import Std
182+
```
183+
179184
This example requires that `Std.Data.HashMap.RawLemmas` is imported.
180185
To keep the code shorter, the `Std` namespace is opened:
181186
```lean
@@ -305,6 +310,10 @@ These operations avoid creating a second reference to the value during modificat
305310

306311
:::example "Modifying Values in Maps"
307312

313+
```imports
314+
import Std
315+
```
316+
308317
```lean
309318
open Std
310319
```

Manual/BasicTypes/Option.lean

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ The {lean}`Option` API makes frequent use of this perspective.
3838

3939
:::example "Options as Nullability"
4040

41+
```imports
42+
import Std
43+
```
44+
4145
```lean -show
4246
open Std (HashMap)
4347
variable {Coll} [BEq α] [Hashable α] (a : α) (b : β) {xs : Coll} [GetElem Coll α β fun _ _ => True] {i : α} {m : HashMap α β}
@@ -64,6 +68,10 @@ In many programming languages, it is important to remember to check for the null
6468
When using {name}`Option`, the type system requires these checks in the right places: {lean}`Option α` and {lean}`α` are not the same type, and converting from one to the other requires handling the case of {lean (type := "Option α")}`none`.
6569
This can be done via helpers such as {name}`Option.getD`, or with pattern matching.
6670

71+
```imports
72+
import Std
73+
```
74+
6775
```lean
6876
def postalCodes : Std.HashMap Nat String :=
6977
.empty |>.insert 12345 "Schenectady"

Manual/Classes/DerivingHandlers.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ Lean includes deriving handlers for the following classes:
9696

9797
::::keepEnv
9898
:::example "Deriving Handlers"
99+
100+
```imports
101+
import Lean.Elab
102+
```
103+
99104
Instances of the {name}`IsEnum` class demonstrate that a type is a finite enumeration by providing a bijection between the type and a suitably-sized {name}`Fin`:
100105
```lean
101106
class IsEnum (α : Type) where

Manual/Coercions.lean

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def Bin.ofNat (n : Nat) : Bin :=
168168
| n + 1 => (Bin.ofNat n).succ
169169
```
170170

171-
```lean -show
171+
```lean -show -keep
172172
--- Internal tests
173173
/-- info: [0, 1, 10, 11, 100, 101, 110, 111, 1000] -/
174174
#check_msgs in
@@ -182,7 +182,8 @@ def Bin.ofNat (n : Nat) : Bin :=
182182
Bin.done.succ.succ.succ.succ.succ.succ,
183183
Bin.done.succ.succ.succ.succ.succ.succ.succ,
184184
Bin.done.succ.succ.succ.succ.succ.succ.succ.succ]
185-
185+
```
186+
```lean -show
186187
def Bin.toNat : Bin → Nat
187188
| .done => 0
188189
| .zero b => 2 * b.toNat
@@ -206,12 +207,13 @@ theorem Bin.ofNat_toNat_eq {n : Nat} : (Bin.ofNat n).toNat = n := by
206207

207208

208209
Even if {lean}`Bin.ofNat` is registered as a coercion, natural number literals cannot be used for {lean}`Bin`:
209-
```lean (name := nineFail) +error
210+
```lean
210211
attribute [coe] Bin.ofNat
211212

212213
instance : Coe Nat Bin where
213214
coe := Bin.ofNat
214-
215+
```
216+
``` lean (name := nineFail) +error
215217
#eval (9 : Bin)
216218
```
217219
```leanOutput nineFail

Manual/Grind.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ example (a b c : Nat) (h₁ : a = b) (h₂ : b = c) :
104104

105105
This proof uses {tactic}`grind`'s commutative ring solver.
106106

107+
```lean -show
108+
open Lean.Grind
109+
```
107110
```lean
108111
example [CommRing α] [NoNatZeroDivisors α] (a b c : α) :
109112
a + b + c = 3

Manual/Grind/Algebra.lean

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ open Lean Grind
3737
```
3838

3939
:::example "Commutative Rings" (open := true)
40+
```lean -show
41+
open Lean.Grind
42+
```
4043
```lean
4144
example [CommRing α] (x : α) : (x + 1) * (x - 1) = x ^ 2 - 1 := by
4245
grind
@@ -45,6 +48,9 @@ example [CommRing α] (x : α) : (x + 1) * (x - 1) = x ^ 2 - 1 := by
4548
:::example "Ring Characteristics" (open := true)
4649
The solver “knows” that `16*16 = 0` because the [ring characteristic](https://en.wikipedia.org/wiki/Characteristic_%28algebra%29) (that is, the minimum number of copies of the multiplicative identity that sum to the additive identity) is `256`, which is provided by the {name}`IsCharP` instance.
4750

51+
```lean -show
52+
open Lean.Grind
53+
```
4854
```lean
4955
example [CommRing α] [IsCharP α 256] (x : α) :
5056
(x + 16)*(x - 16) = x^2 := by
@@ -53,6 +59,9 @@ example [CommRing α] [IsCharP α 256] (x : α) :
5359
:::
5460

5561
:::example "Standard Library Types" (open := true)
62+
```lean -show
63+
open Lean.Grind
64+
```
5665
Types in the standard library are supported by the solver out of the box.
5766
`UInt8` is a commutative ring with characteristic `256`, and thus has instances of {inst}`CommRing UInt8` and {inst}`IsCharP UInt8 256`.
5867
```lean
@@ -62,6 +71,9 @@ example (x : UInt8) : (x + 16) * (x - 16) = x ^ 2 := by
6271
:::
6372

6473
:::example "More Commutative Ring Proofs" (open := true)
74+
```lean -show
75+
open Lean.Grind
76+
```
6577
The axioms of a commutative ring are sufficient to prove these statements.
6678

6779
```lean
@@ -83,6 +95,9 @@ example [CommRing α] (x y : α) :
8395
:::
8496

8597
:::example "Characteristic Zero" (open := true)
98+
```lean -show
99+
open Lean.Grind
100+
```
86101
`ring` proves that `a + 1 = 2 + a` is unsatisfiable because the characteristic is known to be 0.
87102

88103
```lean
@@ -93,6 +108,9 @@ example [CommRing α] [IsCharP α 0] (a : α) :
93108
:::
94109

95110
:::example "Inferred Characteristic" (open := true)
111+
```lean -show
112+
open Lean.Grind
113+
```
96114
Even when the characteristic is not initially known, when `grind` discovers that `n = 0` for some numeral `n`, it makes inferences about the characteristic:
97115
```lean
98116
example [CommRing α] (a b c : α)
@@ -163,7 +181,9 @@ It also rewrites every disequality `p ≠ 0` as the equality `p * p⁻¹ = 1`.
163181
:::
164182

165183
::::example "Fields and `grind`"
166-
184+
```lean -show
185+
open Lean.Grind
186+
```
167187
This example requires its {name}`Field` instance:
168188

169189
```lean
@@ -198,6 +218,9 @@ For example, the polynomial `2 * x * y + 4 * z = 0` is simplified to `x * y + 2
198218
It also used when processing disequalities.
199219

200220
:::example "Using `NoNatZeroDivisors`"
221+
```lean -show
222+
open Lean.Grind
223+
```
201224
In this example, {tactic}`grind` relies on the {name}`NoNatZeroDivisors` instance to simplify the goal:
202225
```lean
203226
example [CommRing α] [NoNatZeroDivisors α] (a b : α) :
@@ -310,6 +333,9 @@ example (x y : Nat) :
310333
Gröbner basis computation can be very expensive. You can limit the number of steps performed by the `ring` solver using the option `grind (ringSteps := <num>)`
311334

312335
:::example "Limiting `ring` Steps"
336+
```lean -show
337+
open Lean.Grind
338+
```
313339
This example cannot be solved by performing at most 100 steps:
314340
```lean +error (name := ring100)
315341
example [CommRing α] [IsCharP α 0] (d t c : α) (d_inv PSO3_inv : α) :

Manual/Grind/Linarith.lean

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ It can be disabled using the option `grind -linarith`.
3838

3939

4040
:::example "Goals Decided by `linarith`" (open := true)
41+
```imports
42+
import Std
43+
```
44+
```lean -show
45+
open Lean.Grind
46+
```
4147
All of these examples rely on instances of the following ordering notation and `linarith` classes:
4248
```lean
4349
variable [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsLinearOrder α]
@@ -69,7 +75,12 @@ example {a b c d e : α} :
6975
:::
7076

7177
:::example "Commutative Ring Goals Decided by `linarith`" (open := true)
72-
78+
```imports
79+
import Std
80+
```
81+
```lean -show
82+
open Lean.Grind
83+
```
7384
For types that are commmutative rings (that is, types in which the multiplication operator is commutative) with {name}`CommRing` instances, `linarith` has more capabilities.
7485

7586
```lean

0 commit comments

Comments
 (0)