Skip to content

Commit bd73cfb

Browse files
Rob23obaTwoFX
andauthored
feat: Decidable as subtype of Bool (leanprover#8309)
This PR changes the definition of `Decidable p` to a structure containing a `Bool` and a proof of either `p` or `¬p`. This is basically the approach proposed by @kmill in leanprover#2038. Due to bugs in the old compiler, this was previously not possible; however, now that the new compiler is enabled, this works perfectly fine. Using `Bool` in the definition of `Decidable` has several advantages, in particular - There are many more definitional equalities, e.g. ```lean variable (a b : Bool) #check (rfl : decide (a = true) = a) #check (rfl : decide (a = false) = !a) #check (rfl : decide (a = true ∧ b = true) = a && b) #check (rfl : decide (a = true ∨ b = true) = a || b) #check (rfl : decide (¬a) = !a) #check (rfl : decide (a = true ↔ b = true) = (a == b)) ``` - The `decide` tactic no longer needs to carry proofs with it, improving performance for well-written `Decidable` instances. - `LawfulBEq` and `DecidableEq` are now compatible: When using the `DecidableEq` instance provided by `LawfulBEq`, `decide (a = b)` is definitionally equivalent to `a == b`. - `Decidable` no longer needs special casing in the compiler. In order to take full advantage from these changes, it is recommended to use the `decidable_of_bool` and `decidable_of_iff` functions to construct `Decidable` instances. This is a breaking change, but in part due to `Decidable.isTrue` and `Decidable.isFalse` remaining as `match_pattern`s, surprisingly few (meta-)programs break. --------- Co-authored-by: Julia Markus Himmel <2065352+TwoFX@users.noreply.github.com>
1 parent 9cc41cb commit bd73cfb

39 files changed

Lines changed: 894 additions & 763 deletions

src/Init/Classical.lean

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,7 @@ is classically true but not constructively. -/
147147
-- This can not be an instance as it would be tried everywhere.
148148
@[instance_reducible]
149149
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
150-
match h with
151-
| isFalse h => isTrue (Classical.not_not.mp h)
152-
| isTrue h => isFalse h
150+
decidable_of_decidable_of_iff not_not
153151

154152
attribute [local instance] decidable_of_decidable_not in
155153
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/

src/Init/Core.lean

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -817,10 +817,12 @@ instance [DecidableEq α] : LawfulBEq α where
817817
Non-instance for `DecidableEq` from `LawfulBEq`.
818818
To use this, add `attribute [local instance 5] instDecidableEqOfLawfulBEq` at the top of a file.
819819
-/
820+
@[instance_reducible]
820821
def instDecidableEqOfLawfulBEq [BEq α] [LawfulBEq α] : DecidableEq α := fun x y =>
821-
match h : x == y with
822-
| false => .isFalse (not_eq_of_beq_eq_false h)
823-
| true => .isTrue (eq_of_beq h)
822+
Decidable.intro (x == y)
823+
(match h : x == y with
824+
| false => not_eq_of_beq_eq_false h
825+
| true => eq_of_beq h)
824826

825827
instance : LawfulBEq Char := inferInstance
826828

@@ -1087,7 +1089,7 @@ theorem Exists.elim {α : Sort u} {p : α → Prop} {b : Prop}
10871089

10881090
/-- Similar to `decide`, but uses an explicit instance -/
10891091
@[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool :=
1090-
decide (h := d)
1092+
d.decide
10911093

10921094
theorem toBoolUsing_eq_true {p : Prop} (d : Decidable p) (h : p) : toBoolUsing d = true :=
10931095
decide_eq_true (inst := d) h
@@ -1143,14 +1145,15 @@ end Decidable
11431145
section
11441146
variable {p q : Prop}
11451147
/-- Transfer a decidability proof across an equivalence of propositions. -/
1146-
@[inline] def decidable_of_decidable_of_iff [Decidable p] (h : p ↔ q) : Decidable q :=
1147-
if hp : p then
1148-
isTrue (Iff.mp h hp)
1149-
else
1150-
isFalse fun hq => absurd (Iff.mpr h hq) hp
1148+
abbrev decidable_of_decidable_of_iff [dp : Decidable p] (h : p ↔ q) : Decidable q where
1149+
decide := decide p
1150+
reflects_decide :=
1151+
match dp with
1152+
| isTrue hp => Iff.mp h hp
1153+
| isFalse hp => fun hq => absurd (Iff.mpr h hq) hp
11511154

11521155
/-- Transfer a decidability proof across an equality of propositions. -/
1153-
@[inline] def decidable_of_decidable_of_eq [Decidable p] (h : p = q) : Decidable q :=
1156+
abbrev decidable_of_decidable_of_eq [Decidable p] (h : p = q) : Decidable q :=
11541157
decidable_of_decidable_of_iff (p := p) (h ▸ Iff.rfl)
11551158
end
11561159

@@ -1161,17 +1164,14 @@ end
11611164
else isTrue (fun h => absurd h hp)
11621165

11631166
@[inline]
1164-
instance {p q} [Decidable p] [Decidable q] : Decidable (p ↔ q) :=
1165-
if hp : p then
1166-
if hq : q then
1167-
isTrue ⟨fun _ => hq, fun _ => hp⟩
1168-
else
1169-
isFalse fun h => hq (h.1 hp)
1170-
else
1171-
if hq : q then
1172-
isFalse fun h => hp (h.2 hq)
1173-
else
1174-
isTrue ⟨fun h => absurd h hp, fun h => absurd h hq⟩
1167+
instance {p q} [dp : Decidable p] [dq : Decidable q] : Decidable (p ↔ q) where
1168+
decide := decide p == decide q
1169+
reflects_decide :=
1170+
match dp, dq with
1171+
| isTrue hp, isTrue hq => ⟨fun _ => hq, fun _ => hp⟩
1172+
| isTrue hp, isFalse hq => fun h => hq (h.1 hp)
1173+
| isFalse hp, isTrue hq => fun h => hp (h.2 hq)
1174+
| isFalse hp, isFalse hq => ⟨fun h => absurd h hp, fun h => absurd h hq⟩
11751175

11761176
/-! # if-then-else expression theorems -/
11771177

@@ -1231,18 +1231,16 @@ instance {c : Prop} {t : c → Prop} {e : ¬c → Prop} [dC : Decidable c] [dT :
12311231
| isFalse hc => dE hc
12321232

12331233
/-- Auxiliary definition for generating compact `noConfusion` for enumeration types -/
1234-
abbrev noConfusionTypeEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f : α → β) (P : Sort w) (x y : α) : Sort w :=
1235-
(inst (f x) (f y)).casesOn
1236-
(fun _ => P)
1237-
(fun _ => P → P)
1234+
abbrev noConfusionTypeEnum {α : Sort u} (f : α → Nat) (P : Sort w) (x y : α) : Sort w :=
1235+
((f x).beq (f y)).casesOn P (P → P)
12381236

12391237
/-- Auxiliary definition for generating compact `noConfusion` for enumeration types -/
1240-
abbrev noConfusionEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f : α → β) {P : Sort w} {x y : α} (h : x = y) : noConfusionTypeEnum f P x y :=
1241-
Decidable.casesOn
1242-
(motive := fun (inst : Decidable (f x = f y)) => Decidable.casesOn (motive := fun _ => Sort w) inst (fun _ => P) (fun _ => P → P))
1243-
(inst (f x) (f y))
1244-
(fun h' => False.elim (h' (congrArg f h)))
1245-
(fun _ => fun x => x)
1238+
abbrev noConfusionEnum {α : Sort u} (f : α → Nat) {P : Sort w} {x y : α} (h : x = y) : noConfusionTypeEnum f P x y :=
1239+
((f x).beq (f y)).casesOn
1240+
(motive := fun b => (f x).beq (f y) = b → b.casesOn P (P → P))
1241+
(fun h' => False.elim (Nat.ne_of_beq_eq_false h' (congrArg f h)))
1242+
(fun _ a => a)
1243+
rfl
12461244

12471245
/-! # Inhabited -/
12481246

@@ -1307,7 +1305,7 @@ theorem recSubsingleton
13071305
{h₂ : ¬p → Sort u}
13081306
[h₃ : ∀ (h : p), Subsingleton (h₁ h)]
13091307
[h₄ : ∀ (h : ¬p), Subsingleton (h₂ h)]
1310-
: Subsingleton (h.casesOn h₂ h₁) :=
1308+
: Subsingleton (h.falseTrueCases h₂ h₁) :=
13111309
match h with
13121310
| isTrue h => h₃ h
13131311
| isFalse h => h₄ h
@@ -1453,14 +1451,15 @@ instance [Inhabited α] [Inhabited β] : Inhabited (MProd α β) where
14531451
instance [Inhabited α] [Inhabited β] : Inhabited (PProd α β) where
14541452
default := ⟨default, default⟩
14551453

1456-
instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
1454+
instance [h : DecidableEq α] [h' : DecidableEq β] : DecidableEq (α × β) :=
14571455
fun (a, b) (a', b') =>
1458-
match decEq a a' with
1459-
| isTrue e₁ =>
1460-
match decEq b b' with
1461-
| isTrue e₂ => isTrue (e₁ ▸ e₂ ▸ rfl)
1462-
| isFalse n₂ => isFalse fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun _ e₂' => absurd (eq_of_heq e₂') n₂
1463-
| isFalse n₁ => isFalse fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun e₁' _ => absurd (eq_of_heq e₁') n₁
1456+
Decidable.intro (decide (a = a') && decide (b = b'))
1457+
(match h a a' with
1458+
| isTrue e₁ =>
1459+
match h' b b' with
1460+
| isTrue e₂ => (e₁ ▸ e₂ ▸ rfl : (a, b) = (a', b'))
1461+
| isFalse n₂ => fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun _ e₂' => absurd (eq_of_heq e₂') n₂
1462+
| isFalse n₁ => fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun e₁' _ => absurd (eq_of_heq e₁') n₁)
14641463

14651464
instance [BEq α] [BEq β] : BEq (α × β) where
14661465
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂
@@ -1611,9 +1610,7 @@ theorem Eq.propIntro {a b : Prop} (h₁ : a → b) (h₂ : b → a) : a = b :=
16111610

16121611
-- Eq for Prop is now decidable if the equivalent Iff is decidable
16131612
instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
1614-
match d with
1615-
| isTrue h => isTrue (propext h)
1616-
| isFalse h => isFalse fun heq => h (heq ▸ Iff.rfl)
1613+
decidable_of_decidable_of_iff ⟨propext, Iff.of_eq⟩
16171614

16181615
/-- Helper theorem for proving injectivity theorems -/
16191616
theorem Lean.injEq_helper {P Q R : Prop} :
@@ -2243,9 +2240,10 @@ instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : ∀ (a b : α),
22432240
fun (q₁ q₂ : Quotient s) =>
22442241
Quotient.recOnSubsingleton₂ q₁ q₂
22452242
fun a₁ a₂ =>
2246-
match d a₁ a₂ with
2247-
| isTrue h₁ => isTrue (Quotient.sound h₁)
2248-
| isFalse h₂ => isFalse fun h => absurd (Quotient.exact h) h₂
2243+
Decidable.intro (decide (a₁ ≈ a₂))
2244+
(match d a₁ a₂ with
2245+
| isTrue h₁ => Quotient.sound h₁
2246+
| isFalse h₂ => fun h => absurd (Quotient.exact h) h₂)
22492247

22502248
/--
22512249
Like `Quot.liftOn q f h` but allows `f a` to "know" that `q = Quot.mk r a`.

src/Init/Data/Bool.lean

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,18 @@ abbrev xor : Bool → Bool → Bool := bne
3232
recommended_spelling "xor" for "^^" in [xor, «term_^^_»]
3333

3434
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∀ x, p x) :=
35-
match inst true, inst false with
36-
| isFalse ht, _ => isFalse fun h => absurd (h _) ht
37-
| _, isFalse hf => isFalse fun h => absurd (h _) hf
38-
| isTrue ht, isTrue hf => isTrue fun | true => ht | false => hf
35+
Decidable.intro (p true && p false)
36+
(match inst true, inst false with
37+
| isFalse ht, _ => fun h => absurd (h _) ht
38+
| isTrue _, isFalse hf => fun h => absurd (h _) hf
39+
| isTrue ht, isTrue hf => fun | true => ht | false => hf)
3940

4041
instance (p : Bool → Prop) [inst : DecidablePred p] : Decidable (∃ x, p x) :=
41-
match inst true, inst false with
42-
| isTrue ht, _ => isTrue ⟨_, ht⟩
43-
| _, isTrue hf => isTrue ⟨_, hf⟩
44-
| isFalse ht, isFalse hf => isFalse fun | ⟨true, h⟩ => absurd h ht | ⟨false, h⟩ => absurd h hf
42+
Decidable.intro (p true || p false)
43+
(match inst true, inst false with
44+
| isTrue ht, _ => ⟨_, ht⟩
45+
| isFalse _, isTrue hf => ⟨_, hf⟩
46+
| isFalse ht, isFalse hf => fun | ⟨true, h⟩ => absurd h ht | ⟨false, h⟩ => absurd h hf)
4547

4648
@[simp] theorem default_bool : default = false := rfl
4749

@@ -636,15 +638,15 @@ protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = tru
636638
cases dp with | _ p => simp [p]
637639

638640
@[bool_to_prop]
639-
theorem and_eq_decide (p q : Bool) : (p && q) = decide (p ∧ q) := by simp
641+
theorem and_eq_decide (p q : Bool) : (p && q) = decide (p ∧ q) := rfl
640642

641643
@[bool_to_prop]
642-
theorem or_eq_decide (p q : Bool) : (p || q) = decide (p ∨ q) := by simp
644+
theorem or_eq_decide (p q : Bool) : (p || q) = decide (p ∨ q) := rfl
643645

644646
@[bool_to_prop]
645-
theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidable p] [dq : Decidable q] :
646-
(decide p == decide q) = decide (p ↔ q) := by
647-
cases dp with | _ p => simp [p]
647+
theorem decide_beq_decide (p q : Prop) [dp : Decidable p] [dq : Decidable q] :
648+
(decide p == decide q) = decide (p ↔ q) :=
649+
rfl
648650

649651
end Bool
650652

src/Init/Data/List/Basic.lean

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,6 @@ theorem of_concat_eq_concat {as bs : List α} {a b : α} (h : as.concat a = bs.c
123123

124124
/-! ## Equality -/
125125

126-
/--
127-
Checks whether two lists have the same length and their elements are pairwise `BEq`. Normally used
128-
via the `==` operator.
129-
-/
130-
protected def beq [BEq α] : List α → List α → Bool
131-
| [], [] => true
132-
| a::as, b::bs => a == b && List.beq as bs
133-
| _, _ => false
134-
135126
@[simp] theorem beq_nil_nil [BEq α] : List.beq ([] : List α) ([] : List α) = true := rfl
136127
@[simp] theorem beq_cons_nil [BEq α] {a : α} {as : List α} : List.beq (a::as) [] = false := rfl
137128
@[simp] theorem beq_nil_cons [BEq α] {a : α} {as : List α} : List.beq [] (a::as) = false := rfl

0 commit comments

Comments
 (0)