-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathlean.lean
More file actions
382 lines (288 loc) · 10.9 KB
/
Copy pathlean.lean
File metadata and controls
382 lines (288 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
import Mathlib
/-
# Logic (Part II)
## `And` and `Or`
In Lean's dependent type theory, `∧` and `∨` serve as
the *direct product* and the *direct sum* in the universe of `Prop`.
Eagle-eyed readers may notice that `∧` and `∨` act similarly to
Cartesian product and disjoint union in set theory.
They are also constructed as inductive types.
-/
section
variable (p q r : Prop)
/-
## `And` (`∧`)
The only constructor of `And` is `And.intro`, which takes a proof of `p` and a proof of `q`
to produce a proof of `p ∧ q`.
Regard this as the *universal property of the direct product* if you like.
`And.intro hp hq` can be abbreviated as `⟨hp, hq⟩`, called the *anonymous constructor*.
`constructor` tactic applies `And.intro` to split the goal `p ∧ q` into subgoals `p` and `q`.
You may also use the anonymous constructor notation `⟨hp, hq⟩` to mean `And.intro hp hq`.
`split_ands` tactic is like `constructor` but works for nested `And`s.
-/
#print And
/- introducing `And` -/
#check And.intro
/- These examples, as introduction rules, are self-evidently true. -/
example (hp : p) (hq : q) : p ∧ q := And.intro hp hq
example (hp : p) (hq : q) : p ∧ q := ⟨hp, hq⟩
example (hp : p) (hq : q) : p ∧ q := by
constructor
· exact hp
· exact hq
/- [EXR] `→`--`∨` distribution. Universal property of the direct product. -/
example (hrp : r → p) (hrq : r → q) : r → p ∧ q := by
intro hr
exact ⟨hrp hr, hrq hr⟩
/-
`And.left` and `And.right` are among the elimination rules of `And`,
which extract the proofs of `p` and `q`.
`rcases hpq with ⟨hp, hq⟩` is a tactic that breaks down the hypothesis
`hpq : p ∧ q` into `hp : p` and `hq : q`.
Equivalently you can use `let ⟨hp, hq⟩ := hpq`.
-/
/- eliminating `And` -/
#check And.left
#check And.right
example (hpq : p ∧ q) : p := hpq.left
example (hpq : p ∧ q) : p := by
rcases hpq with ⟨hp, _⟩
exact hp
example : p ∧ q → p := by
intro ⟨hp, _⟩ -- implicit break-down in `intro`
exact hp
/- [EXR] `And` is symmetric -/
example : p ∧ q → q ∧ p := by
intro hpq
exact ⟨hpq.right, hpq.left⟩
#check And.comm -- above has a name
/- [EXR] `→`--`∨` distribution, in another direction. -/
example (hrpq : r → p ∧ q) : (r → p) ∧ (r → q) := by
constructor
· intro hr
exact (hrpq hr).left
· intro hr
exact (hrpq hr).right
/- nested and -/
example (hpqr : p ∧ q ∧ r) : r := hpqr.right.right
example (hpqr : p ∧ q ∧ r) : r := by
rcases hpqr with ⟨_, ⟨_, hr⟩⟩ -- anonymous constructor can be nested
exact hr
example (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by
exact ⟨hp, ⟨hq, hr⟩⟩
example (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r := by
split_ands
· exact hp
· exact hq
· exact hr
/-
The actual universal elimination rule of `And` is the so-called *decurrification*:
From `(p → q → r)` we may deduce `(p ∧ q → r)`. This is actually a logical equivalence.
Intuitively, requiring both `p` and `q` to deduce `r` is nothing but
requiring `p` to deduce that `q` is sufficient to deduce `r`.
[IGNORE] Decurrification is also self-evidently true in Lean's dependent type theory.
Currification is heavily used in functional programming for its convenience, Lean is no exception.
You are no stranger to decurrification even if you are not a functional programmer:
The *universal property of the tensor product of modules* says exactly the same:
$$
\operatorname{Hom}(M \otimes N, P) \cong \operatorname{Hom}(M, \operatorname{Hom}(N, P))
$$
-/
/- [EXR] currification -/
example (h : p ∧ q → r) : (p → q → r) := by
intro hp hq
exact h ⟨hp, hq⟩
/- [EXR] decurrification -/
example (h : p → q → r) : (p ∧ q → r) := by
intro hpq
exact h hpq.left hpq.right
example (h : p → q → r) : (p ∧ q → r) := by
intro ⟨hp, hq⟩ -- `intro` is smart enough to destructure `And`
exact h hp hq
example (h : p → q → r) : (p ∧ q → r) := by
intro ⟨hp, hq⟩
apply h -- `apply` is smart enough to auto-decurrify and generate two subgoals
· exact hp
· exact hq
/- [IGNORE] decurrification actually originates from `And.rec`, which is self-evident -/
#check And.rec
theorem decurrify (h : p → q → r) : (p ∧ q → r) := And.rec h
/- [EXR] `And.left` is actually a consequence of decurrification -/
example : p ∧ q → p := by
apply decurrify
intro hp _
exact hp
/-
### `Iff` (`↔`), first visit
It's high time to introduce `Iff` here for the first time.
`Iff` (`↔`) contains two side of implications: `Iff.mp` and `Iff.mpr`.
Though it is defined as a distinct inductive type,
`Iff` is very similar to `And` in that you may, somehow, even use it like a `(p → q) ∧ (q → p)`.
The only major difference is the name of the two components.
-/
#check Iff.intro
#check Iff.mp
#check Iff.mpr
example : (p ↔ q) ↔ (p → q) ∧ (q → p) := by
constructor
· intro h
exact ⟨h.mp, h.mpr⟩
· intro ⟨hpq, hqp⟩
exact ⟨hpq, hqp⟩
/-
### `Or` (`∨`)
`Or` has two constructors `Or.inl` and `Or.inr`.
Either a proof of `p` or a proof of `q` produces a proof of `p ∨ q`.
[TODO]
-/
#print Or
#check Or.inl
#check Or.inr
#check Or.elim
#check Or.rec
/- introducing `Or` -/
example (hp : p) : p ∨ q := Or.inl hp
example (hq : q) : p ∨ q := by
right
exact hq
/- elimination rule of `Or`, universal property of the direct sum -/
example (hpr : p → r) (hqr : q → r) : (p ∨ q → r) := fun hpq ↦ (Or.elim hpq hpr hqr)
example (hpr : p → r) (hqr : q → r) : (p ∨ q → r) := (Or.elim · hpr hqr) -- note the use of `·`
example (hpr : p → r) (hqr : q → r) (hpq : p ∨ q) : r := by
apply Or.elim hpq
· exact hpr
· exact hqr
example (hpr : p → r) (hqr : q → r) : (p ∨ q → r) := fun
| Or.inl hp => hpr hp
| Or.inr hq => hqr hq
example (hpr : p → r) (hqr : q → r) (hpq : p ∨ q) : r :=
match hpq with
| Or.inl hp => hpr hp
| Or.inr hq => hqr hq
example (hpr : p → r) (hqr : q → r) (hpq : p ∨ q) : r := by
match hpq with
| Or.inl hp => exact hpr hp
| Or.inr hq => exact hqr hq
example (hpr : p → r) (hqr : q → r) (hpq : p ∨ q) : r := by
cases hpq with
| inl hp => exact hpr hp
| inr hq => exact hqr hq
example (hpr : p → r) (hqr : q → r) (hpq : p ∨ q) : r := by
rcases hpq with (hp | hq) -- `rcases` can also destructure `Or`
· exact hpr hp
· exact hqr hq
example (hpr : p → r) (hqr : q → r) : p ∨ q → r := by
rintro (hp | hq) -- `rintro` is a combination of `intro` and `rcases`
· exact hpr hp
· exact hqr hq
/-
### Comprehensive exercises for `And` and `Or`
[EXR] distributive laws
-/
example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) := by sorry
example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) := by sorry
end
/-
# `Forall` and `Exists`
## Forall (`∀`)
As you may have already noticed, `∀` is just an alternative way of writing `→`.
Say `p` is a predicate on a type `X`, i.e. of type `X → Prop`,
then `∀ x : X, p x` is exactly the same as `(x : X) → p x`.
Though `→` is primitive in Lean's dependent type theory,
we may still (perhaps awkwardly) state the introduction and elimination rules of `∀`:
- Introduction: `fun (x : X) ↦ (h x : p x)` produces a proof of `∀ x : X, p x`.
- Elimination: Given a proof `h` of `∀ x : X, p x`, we can obtain a proof of `p a`
for any specific `a : X`. It is exactly `h a`.
-/
section
variable {X : Type} (p q : X → Prop) (r s : Prop) (a b : X)
#check ∀ x : X, p x
#check ∀ x, p x -- Lean is smart enough to infer the type of `x`
/-
[IGNORE]
Writing `∀` emphasizes that the arrow `→` is of dependent type,
and the domain `X` is a type, not a proposition.
But they are just purely psychological, as the following examples show.
-/
example : (hrs : r → s) → (∀ _ : r, s) := by
intro hrs
exact hrs
/-
## `Exists` (`∃`)
`∃` is a bit more complicated.
Slogan: `∀` is a dependent `→`, `∃` is a dependent `×` (or `∧` in `Prop` universe)
-/
#check ∃ x : X, p x
#check ∃ x, p x -- Lean is smart enough to infer the type of `x`
/-
`∃ x : X, p x` means that we have the following data:
- an element `a : X`;
- a proof `h : p a`.
So a pair `(a, h)` would suffice to construct a proof of `∃ x : X, p x`.
This is the defining introduction rule of `Exists` as an inductive type.
-/
#check Exists.intro
/-
As like `And`, you may use the anonymous constructor notation `⟨a, h⟩` to mean `Exists.intro a h`.
In tactic mode, `use a` make use of `Exists.intro a` to reduce
the goal `∃ x : X, p x` to `p a`.
-/
example (a : X) (h : p a) : ∃ x, p x := Exists.intro a h
example (a : X) (h : p a) : ∃ x, p x := ⟨a, h⟩
example (a : X) (h : p a) : ∃ x, p x := by use a
-- [EXR]
example (x y z : ℕ) (hxy : x < y) (hyz : y < z) : ∃ w, x < w ∧ w < z :=
⟨y, ⟨hxy, hyz⟩⟩
/-
Note that in the defining pair `(a, h)`, `h` is a proof of `p a`, whose type depends on `a`.
Thus psychologically, you may view `∃ x : X, p x` as a dependent pair type `(x : X) × (p x)`.
Have writing `Exists` as a dependent pair type reminded you of the currification process?
Elimination rule:
To construct the implication `(∃ x : X, p x) → q`, it suffices to have a proof of
`(∀ x : X, p x → q)`, i.e. `(x : X) → p x → q`.
In tactic mode, `rcases h with ⟨a, ha⟩` make use of this elimination rule to break down
a hypothesis `h : ∃ x : X, p x` into a witness `a : X` and a proof `ha : p a`.
-/
#check Exists.elim
example : (∀ x, p x → r) → ((∃ x, p x) → r) := by
intro hf he
exact Exists.elim he hf
example : (∀ x, p x → r) → ((∃ x, p x) → r) := by
intro hf he
rcases he with ⟨a, hpa⟩
exact hf a hpa
example : (∀ x, p x → r) → ((∃ x, p x) → r) := by
intro h ⟨a, hpa⟩ -- you may also `rcases` explicitly
exact h a hpa
-- [EXR] reverse direction is also true
example : ((∃ x, p x) → r) → (∀ x, p x → r) := by
intro h a hpa
apply h
use a
-- [EXR]
example : (∃ x, r ∧ p x) → r ∧ (∃ x, r ∧ p x) := by
intro ⟨a, ⟨hr, hpa⟩⟩
exact ⟨hr, ⟨a, ⟨hr, hpa⟩⟩⟩
-- [EXR]
example : (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ (∃ x, q x) := by
constructor
· rintro ⟨a, (hpa | hqa)⟩
· left; use a
· right; use a
· rintro (⟨a, hpa⟩ | ⟨a, hqa⟩)
· use a; left; exact hpa
· use a; right; exact hqa
end
/-
### [IGNORE] A cosmological remark
The pair `(a, h)` actually do not have type `(x : X) × (p x)`.
The latter notation is actually for the *dependent pair type* (or `Sigma` type),
which lives in `Type*` universe.
But `Exists` should live in `Prop`,
and in `Prop` universe we admit *proof-irrelevance*, i.e. we do not save data.
So `Exists` forget the exact witness `a` once it is proved.
This "forgetfulness" is revealed by the fact that there is no elimination rule
`Exists.fst` to extract the witness `a` from a proof of `∃ x : X, p x`,
as long as `X` lives in the `Type*` universe.
(Note that `Exists.elim` can only produce propositions in `Prop`)
-/