Source: Gemini 3.5 prompt: Lean4: Describe in detail the rfl tactic and prove five examples in increasing complexity.
---
The `**rfl**` tactic (short for _reflexivity_) is a workhorse in Lean 4. It discharges equality goals of the form a = b whenever a and b can be simplified or evaluated to be under Lean's computation rules.
### 1. Mathematical and Logical Foundation
In Dependent Type Theory—specifically the **Calculus of Inductive Constructions (CIC)** on which Lean 4 is built—equality comes in two distinct flavors:
#### Definitional Equality ($\equiv$)
Two expressions are **definitionally equal** if they evaluate to the exact same normal form under computational reduction rules:
- $\beta$-reduction: $(\lambda x, e) \ v \rightsquigarrow e[x \mapsto v]$ (function application)
- $\delta$-reduction:** replacing a defined constant/identifier with its definition
- $\iota$-reduction:** pattern matching on inductive types (e.g., structural recursion on `Nat`)
- $\zeta$-reduction expanding `let`-bindings
- $\eta$-reduction: $(\lambda x, f\ x) \rightsquigarrow f$ when x is not free in f
Definitional equality is a meta-theoretic judgments: if a $\equiv$ b, the Lean kernel treats them as inter-exchangeable without requiring a explicit proof object.
#### Propositional Equality (=)
**Propositional equality** is a type/proposition represented by the datatype `Eq a b` (written a = b). It has a single constructor:
$\text{Eq.refl} : \forall \{A : \text{Sort } u\} (a : A), a = a$
Mathematically, `rfl` works by bridging the gap between definitional equality and propositional equality:
> If two terms a and b are **definitionally equal** $(a \equiv b)$, then by replacing b with its definitionally equivalent term a, the goal becomes a = a. This immediately satisfies the constructor $\text{Eq.refl }$ a.
### 2. Lean 4 Implementation Details
In Lean 4, `rfl` is not just a hardcoded kernel rule; it is extensible and tied to the type class mechanism as well as core macro expansions.
```
┌─────────────────────────────┐
│ Goal State: a = b │
└──────────────┬──────────────┘
│
[ Macro / Syntax Expansion ]
│
▼
┌─────────────────────────────┐
│ Is it standard Eq? │
└──────┬───────────────┬──────┘
Yes │ │ No
▼ ▼
┌───────────────────┐ ┌───────────────────────────┐
│ Synthesize │ │ Search for `@[refl]` │
│ `Eq.refl a` proof │ │ theorem in environment │
└─────────┬─────────┘ └─────────────┬─────────────┘
│ │
└─────────────┬─────────────┘
│
▼
┌─────────────────────────────┐
│ Kernel DefEq Check (a ≡ b) │
└─────────────────────────────┘
```
#### Syntax and Macro Expansion
At the tactic level, invoking `rfl` expands to calling `evalRefl` or applying a generalized relation.
1. **Standard Propositional Equality (`Eq`):** When the target goal is a = b, `rfl` emits the kernel term `Eq.refl a`. The typechecker then attempts to unify a and b by checking if $a \equiv b.$
2. **Generalized Reflexivity (`@[refl]`):** Lean 4 extends `rfl` to work for user-defined reflexive relations (like $\le, ~~\equiv, ~~or ~~\leftrightarrow$). When `rfl` encounters a goal like $a \sim b,$ it searches the environment for a theorem marked with the `@[refl]` attribute (such as `Nat.le_refl`).
#### Kernel DefEq Checking & Reduction Hierarchy
When Lean attempts to unify a and b, the elaborator and kernel perform reductions in stages:
1. **DefEq Unification:** It attempts to unfold constants ($\delta$-reduction), substitute variables ($\zeta$-reduction), and compute recursive pattern matches ($\iota$-reduction).
2. **Whnf (Weak Head Normal Form):** Lean reduces both sides to WHNF to inspect their outer constructors.
3. **Transparency Settings:** By default, tactics run under `default` transparency (unfolding non-irreducible definitions). The kernel checks the resulting proof term under strict kernel transparency.
If a and b fail to reduce to identical syntactic forms, the `rfl` tactic throws a unification error: `type mismatch, term Eq.refl a has type a = a but is expected to have type a = b`.
### 3. Five Examples in Increasing Complexity
#### Example 1: Syntactic Identity (Trivial)
The simplest case where both sides of the equality are identical expressions without requiring any computation.
```lean
theorem ex1 (x : Nat) : x = x := by
rfl
```
- **Mechanism:** x and x are already syntactically identical. Lean directly applies `Eq.refl x`.
#### Example 2: Definitional Unfolding ($\delta$-reduction)
Equality where one side uses a defined constant or alias that needs to be expanded.
```lean
def double (n : Nat) : Nat := n + n
theorem ex2 (n : Nat) : double n = n + n := by
rfl
```
- **Mechanism:** The identifier `double` is unfolded ($\delta$-reduction) to `fun n => n + n`. Then function application ($\beta$-reduction) yields `n + n = n + n`.
#### Example 3: Computational Evaluation ($\iota$-reduction on Nat)
Equality relying on recursive computation over structural definitions (like natural number addition).
```lean
theorem ex3 : 2 + 3 = 5 := by
rfl
```
- **Mechanism:** In Lean, `Nat` addition is defined recursively on the second argument:
$n + 0 = n\\\\
n + (m + 1) = (n + m) + 1
$
Lean unfolds `2 + 3` into `Nat.succ (2 + 2)` $\rightarrow$ `Nat.succ (Nat.succ (2 + 1))` $\dots$ down to `5`. Both sides evaluate to `Nat.succ (Nat.succ (Nat.succ (Nat.succ (Nat.succ Nat.zero))))`, matching definitionally.
#### Example 4: Higher-Order Functions ($\beta$, $\zeta$, and $\eta$ Reductions)
Equality involving anonymous functions, `let`-bindings, and tuple projections.
```lean
theorem ex4 (f : Nat → Nat) :
(let g := fun x => f (x + 1); g 4) = f 5 := by
rfl
```
- **Mechanism:**
1. $\zeta$-reduction expands the `let g := ...` binding.
2. The target becomes `(fun x => f (x + 1)) 4 = f 5`.
3. $\beta$-reduction substitutes `4` for `x`: `f (4 + 1) = f 5`.
4. $\iota$-reduction simplifies `4 + 1` to `5`, yielding `f 5 = f 5`.
#### Example 5: Type-Level Computation (Dependent Types & Decidable Props)
Equality involving type-level evaluation, structures, and proofs embedded inside values.
```lean
-- A proof that a boolean statement is true via Decidable computation
theorem ex5 : (if 10 < 20 then "yes" else "no") = "yes" := by
rfl
```
- **Mechanism:**
1. The condition `10 < 20` is evaluated using the `Decidable (10 < 20)` instance (`Decidable.isTrue ...`).
2. The `if ... then ... else ...` construct evaluates its branch based on the synthesized decidability proof object.
3. The expression reduces to `"yes" = "yes"` entirely at compile-time/type-checking time.