Zulip Chat Archive
Stream: general
Topic: how to show mutual theorem
Asei Inoue (Jan 03 2026 at 00:26):
How to show this?
mutual
inductive Even : Nat → Prop where
| zero : Even 0
| succ {n : Nat} (h : Odd n) : Even (n + 1)
inductive Odd : Nat → Prop where
| succ {n : Nat} (h : Even n) : Odd (n + 1)
end
mutual
variable {n : Nat}
theorem Even.exists (h : Even n) : ∃ a, n = 2 * a := by
cases h with
| zero => exists 0
| @succ n h =>
obtain ⟨a, ha⟩ := h.exists -- Environment does not contain `Odd.exists`
exists (a + 1)
grind
theorem Odd.exists (h : Odd n) : ∃ a, n = 2 * a + 1 := by
cases h with
| @succ n h =>
obtain ⟨a, ha⟩ := Even.exists h
exists a
grind
end
Aaron Liu (Jan 03 2026 at 00:35):
somehow the problem is the variable {n : Nat}, if you inline it then it's fine
Jakub Nowak (Jan 03 2026 at 01:04):
Iirc if you use variable then it will use the same variable in the recursive calls. In your case n does change between recursive calls, so you shouldn't be using variable for it.
Niklas Halonen (Jan 03 2026 at 18:23):
Hmm there is indeed something fishy going on with variables in mutual blocks (I didn't even know they were allowed!)
This works (due to autoimplicits), but uncommenting either of the variable declarations will make it fail with the most weirdest of errors
mutual
inductive Even : Nat → Prop where
| zero : Even 0
| succ {n : Nat} (h : Odd n) : Even (n + 1)
inductive Odd : Nat → Prop where
| succ {n : Nat} (h : Even n) : Odd (n + 1)
end
-- variable {n m : Nat}
mutual
-- variable {n m : Nat}
theorem Even.exists (h : Even n) : ∃ a, n = 2 * a := match h with
| .zero => by exists 0
| @Even.succ n h => by
obtain ⟨a, ha⟩ := h.exists
exists (a + 1)
grind
theorem Odd.exists (h : Odd m) : ∃ a, m = 2 * a + 1 := match h with
| @Odd.succ n h => by
obtain ⟨a, ha⟩ := Even.exists h
exists a
grind
end
The error says unknown free variable `_fvar.189` .
Not only this, but using n as the variable name in the latter theorem yields more strange errors when a variable declaration is uncommented...
Jakub Nowak (Jan 03 2026 at 19:28):
I don't think the problem is with mutual. Look at this example:
variable (n : Nat)
/--
error: Function expected at
pred
but this term has type
Nat
Note: Expected a function because this term is being applied to the argument
n
-/
#guard_msgs in
def pred : Nat :=
match n with
| 0 => 0
| n + 1 => @pred n
/-- info: pred (n : Nat) : Nat -/
#guard_msgs in
#check pred
Inside pred the type of pred is just Nat. Not Nat → Nat. Lean won't let you change the value of n between recursive calls. Implicit variables work the same. It's not possible to change them between recursive calls. This is intended behaviour.
Niklas Halonen (Jan 04 2026 at 13:12):
You are correct, this is really surprising to me as I always thought variables are simply "prepended to the list of arguments", i.e. that variable (n : Nat) desugars as
def pred (n : Nat) : Nat :=
match n with
| 0 => 0
| n + 1 => @pred n
But this to me doesn't explain why having variables n and m in the mutual block leads to the weird error.
Jakub Nowak (Jan 04 2026 at 19:17):
Hm, the code wouldn't work anyway, but yeah, it doesn't make sense why the error is "Unknown identifier".
Last updated: Feb 28 2026 at 14:05 UTC