@@ -254,3 +254,317 @@ example (P Q R : Prop) (h₁ : P → Q) (h₂ : P → R) : P → (Q ∧ R) := by
254254-- ... and finally get a simple term proof.
255255example (P Q R : Prop ) (h₁ : P → Q) (h₂ : P → R) : P → (Q ∧ R) :=
256256 fun p => ⟨h₁ p, h₂ p⟩
257+
258+ /-
259+ ## Intermission: The `repeat`, `all_goals`, `try`, and `<;>` tactics
260+
261+ - `repeat tac` repeatedly applies `tac` to the main goals until it fails.
262+ - `all_goals tac` runs `tac` on each goal, concatenating the resulting goals, if any.
263+ - `try tac` attempts to run `tac` without causing failure if it does not apply.
264+ - `tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal.
265+
266+ They are respectively used around 150, 500, 400, and 7000 times in mathlib.
267+ -/
268+
269+ -- We have seen this example before ...
270+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
271+ cases h -- or `obtain ⟨p, q⟩ := h` or `rcases h with ⟨p, q⟩`
272+ constructor
273+ · assumption
274+ · assumption
275+
276+ -- ... but now we can do it more compactly with `repeat` ...
277+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
278+ cases h
279+ constructor
280+ repeat assumption
281+
282+ -- ... or alternatively with `all_goals` ...
283+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
284+ cases h
285+ constructor
286+ all_goals assumption
287+
288+ -- ... or with `<;>`
289+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
290+ cases h
291+ constructor <;> assumption
292+
293+ -- We can also just `try` to execute a tactic.
294+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
295+ obtain ⟨p, q⟩ := h
296+ constructor
297+ all_goals -- This is needed since otherwise `try exact p` would only try to match goal 1
298+ try exact p -- Here the `try` is required ...
299+ try exact q -- ... and here of course the `try` is superfluous,
300+
301+ -- Testing the boundaries
302+
303+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
304+ obtain ⟨p, q⟩ := h
305+ constructor
306+ repeat exact q -- correctly applies to first goal
307+ exact p
308+
309+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
310+ obtain ⟨p, q⟩ := h
311+ constructor
312+ repeat exact p -- works technically but doesn't actually do anything (linter complains)
313+ exact q
314+ exact p
315+
316+ -- This fails: `all_goals` *actually* applies, *repeat* just tried to apply and stops
317+ -- example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
318+ -- obtain ⟨p, q⟩ := h
319+ -- constructor
320+ -- all_goals
321+ -- exact q
322+ -- exact p
323+
324+ -- For the same reason this fails:
325+ -- example (P Q : Prop) (h : P ∧ Q) : Q ∧ P := by
326+ -- obtain ⟨p, q⟩ := h
327+ -- constructor <;> exact p
328+ -- exact q
329+
330+ -- So you need `try` in both the `all_goals` ...
331+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
332+ obtain ⟨p, q⟩ := h
333+ constructor
334+ all_goals
335+ try exact q
336+ exact p
337+
338+ -- ... and the `<;>`
339+ example (P Q : Prop ) (h : P ∧ Q) : Q ∧ P := by
340+ obtain ⟨p, q⟩ := h
341+ constructor <;> try exact p
342+ exact q
343+
344+ /-
345+ Basically: chained `<;>` is the same as an indented `all_goals` block.
346+
347+ * `all_goals` is parallel but fails if something does not fit the expected type
348+ * `repeat` is sequential and stops if something does not fit the expected type
349+ * `all_goals` combined with `try` is parallel and does not fail
350+ -/
351+
352+ /-
353+ ## Working with OR (∨) in the goal
354+
355+ To prove P ∨ Q, we need to prove either P or Q. We can:
356+
357+ - Use `apply Or.inl`/`Or.inr` explicitly
358+ - Use `left`/`right` as shorthand
359+ -/
360+
361+ -- The most explicit way to deal with `∨` in goal is to
362+ -- directly use `apply Or.inl` or `apply Or.inr`
363+ theorem goal_or_apply (P Q : Prop ) (p : P) : P ∨ Q := by
364+ apply Or.inl
365+ exact p
366+
367+ #print goal_or_apply -- gives `Or.inl p`
368+
369+ -- Again note that `apply` is destructive since `apply Or.inr` here
370+ -- would have left us with a goal that cannot be proven from the assumptions.
371+ -- example (P Q : Prop) (p : P) : P ∨ Q := by
372+ -- apply Or.inr
373+ -- ... now we are stuck
374+
375+ -- But we could have argued forward here ..
376+ theorem goal_or_exact (P Q : Prop ) (p : P) : P ∨ Q := by
377+ exact Or.inl p
378+
379+ #print goal_or_exact -- also gives `Or.inl p`
380+
381+ -- .. which also gives the term mode proof.
382+ theorem goal_or_term (P Q : Prop ) (p : P) : P ∨ Q := Or.inl p
383+
384+ #print goal_or_term -- also gives `Or.inl p`
385+
386+ -- Perhaps more intuitive are the `left` and `right` tactics
387+ theorem goal_or_tactic (P Q : Prop ) (p : P) : P ∨ Q := by
388+ left
389+ exact p
390+
391+ #print goal_or_tactic -- also gives `Or.inl p`
392+
393+ /-
394+ ## Working with OR in a hypothesis
395+
396+ To use `h : P ∨ Q`, we can:
397+ - Use `apply Or.elim` explicitly
398+ - Use `cases` and `rcases`
399+ - Use `obtain` with pattern matching
400+ -/
401+
402+ -- We can deal with `∨` in a hypethesis by applying `Or.elim` directly,
403+ -- again using `·` to structure the proof to the sub-goals. Note that
404+ -- `Or.elim {...} (h : a ∨ b) (left : a → c) (right : b → c) : c`
405+
406+ -- Viewing `Or.elim` as a method, the most obvious thing to do is ...
407+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
408+ exact Or.elim h p_to_r q_to_r
409+
410+ -- ... or even just use term mode.
411+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R :=
412+ Or.elim h p_to_r q_to_r
413+
414+ -- But if we want to get towards what we naturally expect, a case distinction,
415+ -- we need to use `apply` ...
416+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
417+ apply Or.elim h
418+ · exact p_to_r -- Note that you do not have `p : P` in the assumptions here ...
419+ · exact q_to_r -- ... and likewise you do not have `q : Q` here.
420+
421+ -- ... but if you really want a case distinction as you expect it, you need.
422+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
423+ apply Or.elim h
424+ · intro p
425+ exact p_to_r p
426+ · intro q
427+ exact q_to_r q
428+
429+ -- Note that `apply` just looks for the output of the applied statement in the
430+ -- goal and makes you prove all the assumptions of the applied statement, so
431+ -- if we did not do the partial application `Or.elim h`, we would have gotten
432+ -- three subgoals, since `Or.elim` takes three arguments.
433+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
434+ apply Or.elim -- no `h` here
435+ · exact h
436+ · exact p_to_r
437+ · exact q_to_r
438+
439+ /-
440+ This show why tactis are good to have: you do not need to remember `Or.elim``
441+ or how exactly it is structured. You just use `cases`, `rcases`, or `cases'`
442+ and get exactly the number of cases in the case distinction that you would expect.
443+ -/
444+
445+ -- We can use the `cases` tactic to do a case distinction on a hypothesis ...
446+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
447+ cases h
448+ · exact p_to_r (by assumption)
449+ · exact q_to_r (by assumption)
450+
451+ -- ... and if we want named variables we can also do proper pattern matching
452+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
453+ cases h with
454+ | inl p => exact p_to_r p
455+ | inr q => exact q_to_r q
456+
457+ -- But most likely you should just use `rcases with _ | _` ...
458+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
459+ rcases h with p | q -- compare to previous `rcases h with ⟨p, q⟩`
460+ · exact p_to_r p
461+ · exact q_to_r q
462+
463+ -- ... or you can use `obatain _ | _ := ...`
464+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
465+ obtain p | q := h -- compare to previous `obtain ⟨p, q⟩ := h`
466+ · exact p_to_r p
467+ · exact q_to_r q
468+
469+ -- Note that `cases'` is likewise marked as deprecated by the linter.
470+ example (P Q R : Prop ) (h : P ∨ Q) (p_to_r : P → R) (q_to_r : Q → R) : R := by
471+ cases' h with p q
472+ · exact p_to_r p
473+ · exact q_to_r q
474+
475+ /-
476+ ## Working with nested structures
477+
478+ For more complex structures, we can:
479+ - Use `rcases` for deep pattern matching
480+ - Use `obtain` with nested patterns
481+ -/
482+
483+ -- This is the brute force way ...
484+ example (P Q R : Prop ) (h : P ∧ Q ∧ R) : Q := by
485+ obtain ⟨_, qr⟩ := h
486+ obtain ⟨q, _⟩ := qr
487+ exact q
488+
489+ -- ... but even with what we have seen there is a nicer (term mode) proof.
490+ example (P Q R : Prop ) (h : P ∧ Q ∧ R) : Q :=
491+ h.right.left -- or `h.2.1`
492+
493+ -- But we can also do the deconstruction of `h` in the assumptions more cleanly:
494+ example (P Q R : Prop ) (h : P ∧ Q ∧ R) : Q := by
495+ obtain ⟨_, ⟨q, _⟩⟩ := h
496+ exact q
497+
498+ -- We can even get rid of the nested brackets ...
499+ example (P Q R : Prop ) (h : P ∧ Q ∧ R) : Q := by
500+ obtain ⟨_, q, _⟩ := h
501+ exact q
502+
503+ -- ... but only because `P ∧ Q ∧ R` was bracketed the "natural" way.
504+ example (P Q R : Prop ) (h : (P ∧ Q) ∧ R) : Q := by
505+ obtain ⟨⟨_, q⟩, _⟩ := h -- here `⟨_, q, _⟩` does not work because of `(P ∧ Q) ∧ R`
506+ exact q
507+
508+ -- Nested patterns also work with `rcases`.
509+ example (P Q R : Prop ) (h : P ∧ Q ∧ R) : Q := by
510+ rcases h with ⟨_, q, _⟩
511+ exact q
512+
513+
514+ /-
515+ ## The `rintro` tactic
516+
517+ `rintro` allows for more complex pattern matching and is
518+ used around 7500 times in mathlib.
519+ -/
520+
521+ -- Mixing `∧` with `∨` can quickly becomes very annoying ...
522+ example (P Q R : Prop ) : (P ∧ Q) ∨ R → P ∨ R := by
523+ intro h
524+ rcases h with pq | r
525+ · obtain ⟨p, q⟩ := pq
526+ left
527+ exact p
528+ · right
529+ exact r
530+
531+ -- ... but we can also do mixed nested patterns with `rcases` ...
532+ example (P Q R : Prop ) : (P ∧ Q) ∨ R → P ∨ R := by
533+ intro h
534+ rcases h with ⟨p, q⟩ | r
535+ · left
536+ exact p
537+ · right
538+ exact r
539+
540+ -- ... or with `obtain`
541+ example (P Q R : Prop ) : (P ∧ Q) ∨ R → P ∨ R := by
542+ intro h
543+ obtain ⟨p, q⟩ | r := h
544+ · left
545+ exact p
546+ · right
547+ exact r
548+
549+ -- But if we also want to do the pattern matching in the
550+ -- `intro` (like we have previously seen) we now need `rintro`.
551+ example (P Q R : Prop ) : (P ∧ Q) ∨ R → P ∨ R := by
552+ rintro (⟨p, q⟩ | r)
553+ · left
554+ exact p
555+ · right
556+ exact r
557+
558+ /-
559+ # Exercise Block B02
560+
561+ Hint: try `rintro` with nested structures
562+ -/
563+
564+ -- Exercise 2.1
565+ example (P Q R S : Prop ) : (P ∨ Q) ∧ (R ∨ S) → (P ∧ R) ∨ (P ∧ S) ∨ (Q ∧ R) ∨ (Q ∧ S) := by
566+ sorry
567+
568+ -- Exercise 2.2
569+ example (P Q R S : Prop ) : ((P ∧ Q) ∨ R) ∧ S → (P ∨ R) ∧ (Q ∨ R) ∧ S := by
570+ sorry
0 commit comments