decide — Decidable Propositions
decide proves decidable propositions by computation. If Lean can compute whether something is true or false, decide handles it. This is your go-to tactic for concrete, checkable facts.
The Idea: Computation as Proof
Some mathematical statements can be checked just by calculating. Is 2 + 2 = 4? Calculate it and see. Is 15 divisible by 3? Check if 15 % 3 = 0. Thedecide tactic turns this intuition into actual proofs.
Instead of constructing a proof by hand, decide asks: "Can I just compute whether this is true?" If yes, the computation itself becomes the proof.
What is Decidability?
A proposition is decidable if there's an algorithm to determine whether it's true or false. In Lean, this is captured by theDecidable typeclass:
1-- The Decidable typeclass2#check @Decidable3-- class Decidable (p : Prop) where4-- decide : Bool5-- -- with proof that decide = true ↔ p67-- Many propositions are decidable8#check (inferInstance : Decidable (2 + 2 = 4))9#check (inferInstance : Decidable (5 < 10))10#check (inferInstance : Decidable ([1, 2, 3].contains 2))When a proposition has a Decidable instance, Lean can evaluate it to true or false. The decide tactic uses this evaluation as a proof.
decide works on any proposition with aDecidable instance. It evaluates the decision procedure and uses the result as a proof.Basic Usage
1-- Numeric comparisons2example : 2 + 2 = 4 := by decide3example : 10 > 5 := by decide4example : 100 % 7 = 2 := by decide5example : 17 ≠ 18 := by decide67-- Boolean conditions8example : true ∧ true := by decide9example : ¬false := by decide10example : true ∨ false := by decide11example : false → true := by decide -- Implication with false premise1213-- List operations (with finite lists)14example : [1, 2, 3].length = 3 := by decide15example : [1, 2, 3].contains 2 = true := by decide16example : [1, 2].head? = some 1 := by decide17example : [].isEmpty = true := by decidedecide vs rfl
Both decide and rfl can prove things by computation, but they work differently:
1-- rfl: definitional equality (computation)2example : 2 + 2 = 4 := rfl34-- decide: also works, but through Decidable5example : 2 + 2 = 4 := by decide67-- ✅ decide can prove more than rfl8example : ¬(3 = 5) := by decide -- decide works9-- example : ¬(3 = 5) := rfl -- rfl wouldn't work directly1011-- ✅ decide handles inequalities12example : 1 < 2 := by decide13-- example : 1 < 2 := rfl -- wouldn't work (< isn't definitional)1415-- ✅ decide can prove membership16example : 3 ∈ [1, 2, 3, 4] := by decide17-- This would be harder with rflWhen to Use Which?
| Situation | Use |
|---|---|
| Equality of computed values | rfl (lighter) |
| Inequalities (<, ≤, etc.) | decide |
| Negations (¬) | decide |
| List/array membership | decide |
| Large computations | native_decide |
Decidable Instances: What Works
Many common types and operations have Decidable instances in the standard library:
1-- Equality on basic types2example : "hello" = "hello" := by decide3example : 'a' = 'a' := by decide4example : (5 : Nat) = 5 := by decide56-- Comparisons on ordered types7example : (5 : Nat) ≤ 10 := by decide8example : ((-3) : Int) < 0 := by decide910-- List membership (for elements with DecidableEq)11example : 3 ∈ [1, 2, 3, 4] := by decide12example : 5 ∉ [1, 2, 3, 4] := by decide13example : 'x' ∈ ['a', 'b', 'x', 'y'] := by decide1415-- Option comparisons16example : (some 5 : Option Nat) ≠ none := by decide17example : (none : Option Nat) = none := by decide1819-- Boolean expressions20example : ([1,2,3].all (· < 10)) = true := by decide21example : ([1,2,3].any (· > 2)) = true := by decideWhen decide Fails
decide fails when the proposition involves variables (unknown values) or types without Decidable instances:
1-- ❌ Variables: Can't compute without knowing the values2-- example (n : Nat) : n = n := by decide -- Fails!3example (n : Nat) : n = n := rfl -- Use rfl instead45-- ❌ Functions don't have DecidableEq by default6-- example (f g : Nat → Nat) : f = f := by decide -- Fails!78-- ❌ Propositions without Decidable instance9-- example (P : Prop) : P ∨ ¬P := by decide -- Fails (classical logic)1011-- ❌ Large structures without deriving DecidableEq12structure Person where13 name : String14 age : Nat15-- example : Person.mk "Alice" 30 = Person.mk "Alice" 30 := by decide -- Fails!decide runs at kernel level—it actually computes the answer. This means it can be slow for large computations.native_decide: For Large Computations
native_decide uses compiled native code instead of Lean's kernel, making it much faster for expensive computations:
1-- native_decide is faster for large computations2example : (List.range 1000).length = 1000 := by native_decide34-- Primality testing (expensive to check in kernel)5example : Nat.Prime 97 := by native_decide6example : ¬Nat.Prime 100 := by native_decide78-- Large list operations9example : (List.range 100).sum = 4950 := by native_decide1011-- Checking all elements of a large list12example : (List.range 50).all (· < 100) := by native_decidenative_decide trusts the compiler—it doesn't verify the computation in the kernel. For critical proofs where correctness is paramount, prefer decide.Creating Decidable Instances
For your own types, you can derive or define Decidableinstances to enable decide:
Deriving DecidableEq
1-- Automatically generate equality decisions2structure Person where3 name : String4 age : Nat5deriving DecidableEq -- Now decide works on Person equality!67example : (Person.mk "Alice" 30) = (Person.mk "Alice" 30) := by decide8example : (Person.mk "Alice" 30) ≠ (Person.mk "Bob" 25) := by decide910-- Works for enums too11inductive Color12 | red | green | blue13deriving DecidableEq1415example : Color.red ≠ Color.blue := by decideCustom Decidable Instances
1-- Define your own decidable predicates2def isEven (n : Nat) : Prop := n % 2 = 034-- Make it decidable by connecting to Bool5instance (n : Nat) : Decidable (isEven n) := 6 if h : n % 2 = 0 then isTrue h else isFalse h78example : isEven 4 := by decide9example : ¬isEven 5 := by decideDeep Dive: How decide Works Under the Hood
decide extracts a proof from aDecidable instance through these steps:
- Find a
Decidable pinstance for propositionp - Evaluate
decide pto gettrueorfalse - If
true, extract the proof ofpfrom the instance - If
false, extract the proof of¬p
1-- The key lemma used by decide2#check @of_decide_eq_true3-- of_decide_eq_true : decide p = true → p45-- So decide:6-- 1. Evaluates "decide p" to true (or false)7-- 2. If true, applies of_decide_eq_true8-- 3. The kernel verifies that decide p really equals true910-- This is why it's slower than rfl—it involves more stepsPractical Examples
Finite Verification
Sometimes you need to check a property holds for all values in a finite set:
1-- Check a property for specific values2example : ∀ n ∈ [0, 1, 2, 3, 4], n < 5 := by decide34-- Verify a truth table5example : ∀ a ∈ [true, false], ∀ b ∈ [true, false], 6 a && b = b && a := by decide78-- Check small cases exhaustively9example : ∀ n ∈ [0, 1, 2, 3], n * n ≤ 9 := by decideArray and String Operations
1-- Prove array operations are valid2def myArray : Array Nat := #[1, 2, 3, 4, 5]34example : myArray.size = 5 := by native_decide56-- String checks7example : "hello".length = 5 := by decide8example : "hello".startsWith "hel" := by decide9example : "world".contains 'o' := by decide10example : "lean4".endsWith "4" := by decideSimple Logic
1-- Quick proofs of simple logical facts2example : ¬(true ∧ false) := by decide3example : true ∨ false := by decide4example : false → true := by decide5example : ¬¬true := by decide67-- De Morgan's law for specific values8example : ¬(true ∧ false) = (¬true ∨ ¬false) := by decideCommon Mistakes
Using decide with Variables
1-- ❌ Wrong: can't decide facts about unknown values2-- example (n : Nat) : n = n := by decide34-- ✅ Right: use rfl for reflexive equality5example (n : Nat) : n = n := rflExpecting decide to Handle Symbolic Reasoning
1-- ❌ Wrong: decide only computes, doesn't reason symbolically2-- example (n : Nat) : n + 0 = n := by decide34-- ✅ Right: use simp or omega for symbolic properties5example (n : Nat) : n + 0 = n := by simp6example (n : Nat) : n + 0 = n := by omegaUse decide to prove this membership fact.
1example : 'a' ∈ ['a', 'b', 'c'] := by decideUse decide to prove a small propositional statement.
1example : (2 = 3) ∨ (2 = 2) := by decideUse decide to verify a string property.
1example : "hello".length > 3 := by decide