In **Lean 4**, `congrArg` is a fundamental lemma and concept from core logic,
often invoked explicitly via `apply congrArg` or utilized inside `conv` mode as the `arg` tactic.
It represents the mathematical principle of **congruence of arguments**: if two inputs are equal 𝑥=𝑦
, then applying the exact same function f to both inputs will yield equal results 𝑓(𝑥)=𝑓(y).
Its underlying type signature in the core library is:
lean
```
theorem congrArg {α : Sort u} {β : Sort v} (f : α → β) {x y : α} (h : x = y) : f x = f y
```
Use code with caution.
When you use `apply congrArg` on a goal of the form `⊢ f x = f y`, Lean matches the function f
on both sides and changes your goal to proving **`⊢ x = y`**.
---
5 Examples of Increasing Complexity
1. Basic Application (Stripping a Function)
This is the most straightforward use case. If you have an equality in your hypothesis and
need to show that the same function applied to both sides is equal, you strip away the function.
lean
```
example (x y : Nat) (h : x = y) : x + 1 = y + 1 := by
-- The target function f is `fun n => n + 1`
apply congrArg
-- Goal becomes: ⊢ x = y
exact h
```
Use code with caution.
2. Working with Multi-Argument Functions
When a function takes multiple arguments, `congrArg` matches against the _last_ explicit argument.
The preceding arguments are bundled into the function f itself.
lean
```
example (a b : Nat) (h : a = b) : 5 * a + 2 = 5 * b + 2 := by
-- The target function f is `fun x => 5 * x + 2`
apply congrArg
-- Goal becomes: ⊢ a = b
exact h
```
Use code with caution.
3. Deeply Nested Structures (Chained `congrArg`)
When equalities are nested deeper inside expressions, you can
apply `congrArg` sequentially to strip layers of function applications step by step.
lean
```
def wrap (n : Nat) : List Nat := [n]
example (x y : Nat) (h : x = y) : [wrap x] = [wrap y] := by
-- Goal: ⊢ [wrap x] = [wrap y] (which is List.cons (wrap x) [])
-- Top-level function f is `fun L => [L]`
apply congrArg
-- Goal becomes: ⊢ wrap x = wrap y
apply congrArg
-- Goal becomes: ⊢ x = y
exact h
```
Use code with caution.
4. Targeting Explicit Sub-arguments in `conv` Mode
In complex proofs, you may want to rewrite a specific argument inside a term without affecting anything else.
Inside conversion (`conv`) mode, `arg` acts as a targeted structural variant of `congrArg`.
lean
```
example (a b c : Nat) (h : b = c) : a * b + 1 = a * c + 1 := by
conv =>
lhs -- Target the Left Hand Side: `a * b + 1`
arg 1 -- Target the 1st argument of `+`: `a * b`
arg 2 -- Target the 2nd argument of `*`: `b`
-- The state is now isolated to: `⊢ b`
rw [h] -- Changes `b` to `c`
-- The goal is now unified to: ⊢ a * c + 1 = a * c + 1
rfl
```
Use code with caution.
5. Combining `congrArg` with Structure Constructors
This example shows how `congrArg` interacts with data structures
(like `Prod` or custom inductive types). Lean treats constructors as functions,
meaning you can strip a constructor wrapper down to its elements.
lean
```
structure Point where
x : Nat
y : Nat
example (a b : Nat) (h : a = b) : Point.mk a 10 = Point.mk b 10 := by
-- The function f here is `fun entries => Point.mk entries 10`
-- because the second argument (10) is identical on both sides.
apply congrArg
-- Goal becomes: ⊢ a = b
exact h
```
Use code with caution.
_
(Note: For goals where multiple different arguments vary simultaneously—e.g., `⊢ f a b = f c d`
—you should instead use the more robust **`congr`** or **`congr!`** tactics,
which automatically handle complex multi-variable structural splitting)
Would you like to explore how **`congrArg`** differs from **`congrFun`**,
or see how the general **`congr`** automation tactic simplifies proofs like Example 3 into a single line?