Zulip Chat Archive

Stream: new members

Topic: Is there an un-Classical version of the `tauto` tactic?


Lars Ericson (Dec 30 2023 at 06:05):

Consider:

import Mathlib

variable (p q r : Prop)

theorem ta (hpqpr: (p  q)  (p  r)): p  (q  r) :=
  by tauto

theorem tb (hpqpr: (p  q)  (p  r)): p  (q  r) :=
  or_and_left.2 hpqpr

#check ta -- (p q r : Prop) (hpqpr : (p ∨ q) ∧ (p ∨ r)) : p ∨ q ∧ r
#check tb -- the same as ta

#explode ta
#explode tb

The #explode ta gives a proof which uses em:

ta :  (p q r : Prop), (p  q)  (p  r)  p  q  r

0            p                    Prop
1            q                    Prop
2            r                    Prop
3            hpqpr                (p  q)  (p  r)
4            em                   (a : Prop)  Decidable a
5            left                  p  q
6            right                 p  r
7            h                      p
8            h                       p
9            or_iff_not_imp_left      p  q  r  ¬p  q  r
10           a                        ¬p
118,10       absurd                    q
128,10       absurd                    r
1311,12      And.intro                 q  r
1410,13      I                       ¬p  q  r
159,14       Iff.mpr                  p  q  r
168,15       I                      p  p  q  r
17           h                       r
18           a                        ¬p
197,18       absurd                    q
2019,17      And.intro                 q  r
2118,20      I                       ¬p  q  r
229,21       Iff.mpr                  p  q  r
2317,22      I                      r  p  q  r
246,16,23    Or.casesOn              p  q  r
257,24       I                     p  p  q  r
26           h                      q
27           h                       p
28           a                        ¬p
2927,28      absurd                    r
3026,29      And.intro                 q  r
3128,30      I                       ¬p  q  r
329,31       Iff.mpr                  p  q  r
3327,32      I                      p  p  q  r
34           h                       r
35           a                        ¬p
3626,34      And.intro                 q  r
3735,36      I                       ¬p  q  r
389,37       Iff.mpr                  p  q  r
3934,38      I                      r  p  q  r
406,33,39    Or.casesOn              p  q  r
4126,40      I                     q  p  q  r
425,25,41    Or.casesOn             p  q  r
435,6,42     I                    p  q  p  r  p  q  r
443,43       And.casesOn           p  q  r
454,44       I                   ((a : Prop)  Decidable a)  p  q  r
4645         letFun               p  q  r
470,1,2,3,46 I                    (p q r : Prop), (p  q)  (p  r)  p  q  r

The #explode tb gives a more terse proof which is not Classical:

tb :  (p q r : Prop), (p  q)  (p  r)  p  q  r

0          p            Prop
1          q            Prop
2          r            Prop
3          hpqpr        (p  q)  (p  r)
4          or_and_left  p  q  r  (p  q)  (p  r)
54,3       Iff.mpr      p  q  r
60,1,2,3,5 I            (p q r : Prop), (p  q)  (p  r)  p  q  r

Is there a version of tauto which limits itself to what it can do without em?

Newell Jensen (Dec 30 2023 at 06:24):

Try itauto

Lars Ericson (Dec 30 2023 at 16:00):

Perhaps intuitionistic logic is no longer top of mind. itauto gives tactic 'Mathlib.Tactic.itauto' has not been implemented in

import Mathlib

variable (p q r : Prop)

theorem ta (hpqpr: (p  q)  (p  r)): p  (q  r) :=
  by itauto -- tactic 'Mathlib.Tactic.itauto' has not been implemented

Henrik Böving (Dec 30 2023 at 16:08):

generally speaking the focus on having an un classical tool in Lean if there is a classical one available is very low right now.

Lars Ericson (Dec 30 2023 at 17:11):

In terms of what I was looking at above, I don't actually know that or_and_left.2 hpqpr is un-Classical, because it is not really exploding the or_and_left.2 in

4          or_and_left  p  q  r  (p  q)  (p  r)
54,3       Iff.mpr      p  q  r
60,1,2,3,5 I            (p q r : Prop), (p  q)  (p  r)  p  q  r

Is there an alternative to #explode which will trace one level down into or_and_left in

import Mathlib

variable (p q r : Prop)
variable (hpqpr: (p  q)  (p  r))

#explode or_and_left.2 hpqpr -- need trace one level deeper

Kyle Miller (Dec 30 2023 at 17:17):

No, #explode doesn't have any features to unfold theorems. It could be implemented, but I'm not sure what the use case is. Is following it up with #explode or_and_left not sufficient for you?

Kyle Miller (Dec 30 2023 at 17:18):

If you care about classical axioms, there's also #print axioms or_and_left, which gives you the answer more directly.

Lars Ericson (Dec 30 2023 at 17:36):

@Kyle Miller, I am going through these exercises in the Theorem Proving in Lean 4 book. The exercises are divided into un-Classical

example : p  (q  r)  (p  q)  (p  r) := sorry

and Classical ones:

open Classical

variable (p q r : Prop)

example : (p  q  r)  ((p  q)  (p  r)) := sorry

I am just being obsessive-compulsive in trying to verify that the un-Classical exercise is actually un-Classical. I am stuck because I want to do this by cases, which is Classical. tauto also uses em. I am looking at #explode of or_and_left because my training so far in Theorem Proving in Lean 4 is insufficient to be able to read this definition, which has some very terse syntax which hasn't been introduced yet in the book. If I can't read it, I can't sugar it into something that I can reproduce, which is why I am looking for a trace:

import Mathlib

/-- `∨` distributes over `∧` (on the left). -/
theorem or_and_left : a  (b  c)  (a  b)  (a  c) :=
  Or.rec (fun ha => .inl ha, .inl ha⟩) (.imp .inr .inr),
   And.rec <| .rec (fun _ => .inl ·) (.imp_right  .intro)⟩

Ruben Van de Velde (Dec 30 2023 at 17:53):

If you have a term mode proof like this and want to sugar it, you can do it step by step by adding by exact at the start of the proof and then incrementally work though it

Ruben Van de Velde (Dec 30 2023 at 17:53):

The pointy brackets are equivalent to constructor

Kyle Miller (Dec 30 2023 at 17:55):

Kyle Miller said:

If you care about classical axioms, there's also #print axioms or_and_left, which gives you the answer more directly.

Let me mention this command again, since that will tell you whether it depends on Classical.choice

Ruben Van de Velde (Dec 30 2023 at 17:56):

(the point is that at every step, you have a working proof you can inspect)

Lars Ericson (Dec 31 2023 at 00:49):

Without deconstructing or_and_left, I tried to do it again and I came down to a point where I want to do reasoning by cases on p ∨ q. I don't know if proof by cases in this instance requires em because it's not exactly saying p ∨ ¬p, it's just saying that if I know p ∨ q then either p or q must be true, which seems like a minimum expectation of . Then I asked Google Bard how to do proof by cases on disjunction in Lean 4 and it gave me rcases, but I couldn't figure out from docs#rcases how to use it in Lean 4. There are no examples of rcases in Theorem Proving in Lean 4. I did stumble on Or.by_cases in Mathlib, which seems promising but then I got another roadblock: Lean 4 gave me error failed to synthesize instance Decidable p in

import Mathlib

theorem L1l1 (hp: p) : p  q := Or.inl hp

theorem L1l2 (hqr: q  r) : p  q := Or.inr hqr.left

theorem L1l (hpqr: p  (q  r)) : (p  q) := Or.elim hpqr L1l1 L1l2

theorem L1r1 (hqr: q  r) : p  r := Or.inr hqr.right

theorem L1r (hpqr: p  (q  r)) : (p  r) := Or.elim hpqr L1l1 L1r1

theorem L1 (hpqr: p  (q  r)) : (p  q)  (p  r) :=
  And.intro (L1l hpqr) (L1r hpqr)

theorem L2 (hpqpr: (p  q)  (p  r)): p  (q  r) :=
  have hpq : p  q := hpqpr.left
  have hpr : p  r := hpqpr.right
  Or.by_cases hpq -- failed to synthesize instance Decidable p

example : p  (q  r)  (p  q)  (p  r) :=
  Iff.intro L1 L2

Is there a way to make Or.by_cases work or another method of doing the same? I need to do a nested proof with four cases p,p, p,r, q,p and q,r, i.e. it is roughly an Or.by_cases hpq (Or.by_cases hpr) (Or.by_cases hpr).

Mario Carneiro (Dec 31 2023 at 00:52):

there are many examples of rcases in mathlib

Mario Carneiro (Dec 31 2023 at 00:53):

Did you see docs#Std.Tactic.rcases ? What in the documentation was unclear?

Lars Ericson (Dec 31 2023 at 01:00):

@Mario Carneiro I'm just finding this a bit hard to read, and it doesn't walk through a complete example that for example would run self-contained in Lean 4 Playground. I can look for full examples by grepping through Mathlib. It would be very hepful if the Mathlib docs had the ability to push a button to pop examples into Lean 4 Playground in the same way as the button on Zulip works for inline code examples.

I think I've made a little more progress anyway. It occurred to me that I might be able to do a nested proof using Or.elim. This is where I'm at now, with one sorry in lemmaL2b:

import Mathlib

theorem L1l1 (hp: p) : p  q := Or.inl hp

theorem L1l2 (hqr: q  r) : p  q := Or.inr hqr.left

theorem L1l (hpqr: p  (q  r)) : (p  q) := Or.elim hpqr L1l1 L1l2

theorem L1r1 (hqr: q  r) : p  r := Or.inr hqr.right

theorem L1r (hpqr: p  (q  r)) : (p  r) := Or.elim hpqr L1l1 L1r1

theorem L1 (hpqr: p  (q  r)) : (p  q)  (p  r) :=
  And.intro (L1l hpqr) (L1r hpqr)

theorem L2a (hp : p) : p  q  r := Or.inl hp

theorem L2b (hpq : p  q) (hpr : p  r) (hq : q) : p  q  r := sorry

theorem L2 (hpqpr: (p  q)  (p  r)): p  (q  r) :=
  have hpq : p  q := hpqpr.left
  have hpr : p  r := hpqpr.right
  Or.elim hpq
    L2a
    (L2b hpq hpr)

example : p  (q  r)  (p  q)  (p  r) :=
  Iff.intro L1 L2

Mario Carneiro (Dec 31 2023 at 01:06):

There are standalone simple examples in https://github.com/leanprover/std4/blob/main/test/rcases.lean

Mario Carneiro (Dec 31 2023 at 01:09):

in any case, the short answer to your question is that the way to use rcases on an or hypothesis is rcases hpq with h | h

Mario Carneiro (Dec 31 2023 at 01:12):

here's a proof of L2 using rcases:

theorem L2 (hpqpr: (p  q)  (p  r)): p  (q  r) := by
  rcases hpqpr with hp | hq, hp | hr
  · exact .inl hp
  · exact .inl hp
  · exact .inl hp
  · exact .inr hq, hr

Mario Carneiro (Dec 31 2023 at 01:15):

by the way, since you asked: case analysis on p ∨ q is intuitionistically valid, it does not require em and if you use #print axioms L2 it will verify this

Mario Carneiro (Dec 31 2023 at 01:17):

This is the kind of "proof by cases" that is intuitionistically okay. by_cases is not intuitionistically valid because it first uses em to magic up a proof of p ∨ ¬p and then case splits on it. The case split is not the troublesome part, it is obtaining the p ∨ ¬p to split

Lars Ericson (Dec 31 2023 at 04:25):

Thank you @Mario Carneiro. I will bookmark the test directory!

I am still trying to unpack the syntax of or_and_left.2. There are a lot of advanced features in this:

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c)  :=
  And.rec <| .rec (fun _ => .inl ·) (.imp_right  .intro)

First the dot notation with no left hand side. Hovering over the .recs and .inls shows that these can be expanded to

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c)  :=
  And.rec <| Or.rec (fun _ => Or.inl ·) (Or.imp_right  And.intro)

Then the pipeline notation <| which is explained in the Lean Manual (now also bookmarked) here. In this case it eliminates one parenthesis, so above can be rewritten as:

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c)  :=
  And.rec (Or.rec (fun _ => Or.inl ·) (Or.imp_right  And.intro))

The composition operator is documented in Logic and Proof (now bookmarked). So g ∘ f is the same as fun x => g(fx)) giving

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c)  :=
  And.rec (Or.rec (fun _ => Or.inl ·) (fun x => Or.imp_right (And.intro x)))

The center dot · is documented in Functional Programming in Lean., according to which Or.inl · is the same as fun x => Or.inl x, giving:

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c)  :=
  And.rec (Or.rec (fun _ => (fun x => Or.inl x)) (fun x => Or.imp_right (And.intro x)))

Unfortunately this doesn't work, the error is

application type mismatch
  Or.inl x
argument
  x
has type
  a  c : Prop
but is expected to have type
  a : Prop

The .rec notation is also unfamiliar. It is not used in Theorem Proving in Lean, the Lean Manual, or Functional Programming in Lean, as far as I can see. I don't see it in docs#And. Where is it described? I see it used in the source.

Mario Carneiro (Dec 31 2023 at 04:28):

Your unpacking of the dot notation is not quite right. The way it works is that you have an expression surrounded in parentheses with a · somewhere in it, and this means to replace the · with a fresh variable x and add fun x => at the parentheses. So (fun _ => Or.inl ·) is equivalent to fun x => fun _ => Or.inl x

Mario Carneiro (Dec 31 2023 at 04:29):

And.rec is the recursor for And, it is defined by the inductive command (every inductive type has a T.rec function)

Mario Carneiro (Dec 31 2023 at 04:31):

the type of the rec function follows the definition of the inductive type in a particular way; for And this will look something like (A -> B -> C) -> (A /\ B) -> C which is the same as the And.elim function, except that it allows C to depend on the proof of A /\ B (this usually isn't used and can be ignored for the most part)

Lars Ericson (Dec 31 2023 at 15:19):

Thank you @Mario Carneiro I see .rec discussed now in Theorem proving in Lean 4 in the Inductive Types section.

Thanks also for the reading of ·, that is very helpful.

Lars Ericson (Dec 31 2023 at 16:19):

Here it is mostly un-golfed (flogged?). I will play close attention to .rec when I get to the inductive chapter:

import Mathlib

lemma L1a : a  a  c  a  b  c :=
  fun ha: a =>
  fun _ : a  c =>
  Or.intro_left (b  c) ha

lemma L1b : b  a  c  a  b  c :=
  fun hb : b =>
  Or.imp_right (And.intro hb)

lemma L1 : a  b  a  c  a  b  c := Or.rec L1a L1b

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c) :=
   And.rec L1 -- And.rec <| .rec (fun _ => .inl ·) (.imp_right ∘ .intro)

Mario Carneiro (Dec 31 2023 at 16:21):

Are you aware that you can use have to introduce intermediary statements? You don't need to make definitions for everything (and it's better not to, since your short names like L1 will cause name clashes)

Mario Carneiro (Dec 31 2023 at 16:23):

theorem or_and_left2 : (a  b)  (a  c)   a  (b  c) :=
  have L1a : a  a  c  a  b  c :=
    fun ha: a =>
    fun _ : a  c =>
    Or.intro_left (b  c) ha
  have L1b : b  a  c  a  b  c :=
    fun hb : b =>
    Or.imp_right (And.intro hb)
  have L1 : a  b  a  c  a  b  c := Or.rec L1a L1b
  And.rec L1

Lars Ericson (Dec 31 2023 at 16:34):

Thanks @Mario Carneiro . I wanted to use a have but it didn't occur to me I could put whole proofs under the have. In examples, have is most often used to introduce terms, like have hp : p := h.left.

Mario Carneiro (Dec 31 2023 at 16:34):

but those are terms

Mario Carneiro (Dec 31 2023 at 16:34):

    fun ha: a =>
    fun _ : a  c =>
    Or.intro_left (b  c) ha

is a term

Eric Wieser (Dec 31 2023 at 17:42):

Should hightlighting use different colors for term and tactic keywords to make this kind of thing less confusing?

Mario Carneiro (Dec 31 2023 at 17:44):

I'm not sure we want to draw that kind of attention to it, it is deliberate design that have and let and if and match and sorry work the same in term and tactic mode

Kevin Buzzard (Dec 31 2023 at 18:45):

And rfl (which used to have two spellings)

Kevin Buzzard (Dec 31 2023 at 18:46):

(as did sorry a very very long time ago)

Mario Carneiro (Dec 31 2023 at 18:46):

have and let too: the tactics used to be called note and pose respectively, I think from coq

Mario Carneiro (Dec 31 2023 at 18:48):

admit still exists today, although IMO we should deprecate and remove it

ZHAO Jiecheng (Jan 02 2024 at 02:00):

Lars Ericson said:

Thanks Mario Carneiro . I wanted to use a have but it didn't occur to me I could put whole proofs under the have. In examples, have is most often used to introduce terms, like have hp : p := h.left.

It's important to understand that "Theorem Proving in Lean 4" isn't a short cut to learn logic or math, it just help you to understand lean. It is unusual that one wants to learn non-em tactic before he established the understanding of term.

Maybe you should just follow the book first. Do not stay too long in a chapter to try to understand everything. Actually, in Chapter 5 you will learn tactics and it will give you exercises to rewrite all the exercises in Chapter 3 and 4 into tactic way. Then you may feel much better.


Last updated: May 02 2025 at 03:31 UTC